Python 2使用什么方法来打印元组?

14

Python的print语句通常打印其输入的repr()。元组似乎不是一个例外:

>>> print (1, 2, 3)
(1, 2, 3)
>>> print repr((1, 2, 3))
(1, 2, 3)

但后来我在研究CPython内部时发现了一些奇怪的行为。简而言之:如果你让Python 2创建一个自引用元组,直接打印它的表现与打印它的repr()/str()/unicode()表示形式完全不同。

>>> print outer   # refer to the link above
((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
... many lines later ...
((((((((((Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError: stack overflow
>>> print repr(outer)
((...),)
>>> print str(outer)
((...),)
>>> print unicode(outer)
((...),)

那么print到底在做什么?为了回答这个问题,我参考了语言参考:

6.6. print语句

print逐个地评估每个表达式,并将结果对象写入标准输出(参见下文)。如果一个对象不是字符串,则首先使用字符串转换规则将其转换为字符串。

字符串转换规则如下:

5.2.9. 字符串转换

字符串转换是一个用反引号(也叫反撇号)括起来的表达式列表:

string_conversion ::=  "`" expression_list "`"

但是用反引号括起来的outer与调用repr()和相关函数得到的结果相同。没有效果。那么print究竟在幕后做了什么呢?

(有趣的是,这种行为在Python 3中被“修复”:打印自己引用的元组会给出省略号截断的形式。)


当我尝试运行你的代码时,出现了struct.error: 'I' format requires 0 <= number <= 4294967295的错误。 - thefourtheye
现在它在 c_outer[inner_index:inner_index+4] = struct.pack('Q', id(outer)) 处失败,显示 ValueError: Can only assign sequence of same size - thefourtheye
尝试将+4替换为+8编辑:我已更新链接的Gist,现在应该可以在32位和64位平台上运行。 - ashastral
这个问题已经在Python 2中报告并被拒绝为“不予修复”。 http://bugs.python.org/issue1069092 - Ned Deily
2
抱歉,我应该仔细查看的。堆栈跟踪显示,堆栈溢出是由internal_print(大约在object.c: 315附近)和tupleprint(大约在tupleobject.c: 253附近)之间的递归调用循环引起的。Python 3.2中解决了递归容器reprs问题:http://bugs.python.org/issue9840。 - Ned Deily
显示剩余5条评论
1个回答

7
你可以通过反汇编Python字节码来了解实际发生的情况。
>>> from dis import dis
>>> dis(compile('print outer', '<string>', 'exec'))
  1           0 LOAD_NAME                0 (outer)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE

我将翻译为:

并阅读底层操作码的源代码。

PRINT_ITEM 最终到达此代码块:

else if (Py_TYPE(op)->tp_print == NULL) {
    PyObject *s;
    if (flags & Py_PRINT_RAW)
        s = PyObject_Str(op);
    else
        s = PyObject_Repr(op);
    ...
}
else
    ret = (*Py_TYPE(op)->tp_print)(op, fp, flags);

这意味着只有当对象的类型没有tp_print函数时,才会调用__str__或__repr__。而tupleobject具有one
如果您想了解CPython的内部工作原理,最好的方法是阅读源代码。我推荐一系列关于Python内部的教程,它解释了您必须了解的所有内容,以完全理解python dis函数的输出。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接