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

Fra*_*til 14 python cpython self-reference python-2.7 python-internals

Python的print声明通常似乎打印repr()其输入.元组似乎不是例外:

>>> print (1, 2, 3)
(1, 2, 3)
>>> print repr((1, 2, 3))
(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

但是当我弄乱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)
((...),)
Run Code Online (Sandbox Code Playgroud)

到底是print做什么的?为了尝试自己回答这个问题,我提到了语言参考:

6.6.该print声明

print依次计算每个表达式并将结果对象写入标准输出(见下文).如果对象不是字符串,则首先使用字符串转换规则将其转换为字符串.

字符串转换的规则是:

5.2.9.字符串转换

字符串转换是一个反向(又称向后)引号括起来的表达式列表:

string_conversion ::=  "`" expression_list "`"
Run Code Online (Sandbox Code Playgroud)

但封闭outer在后面的引号与调用repr()和朋友的结果相同.没有骰子.那么print幕后真的在做什么呢?

(有趣的是,Python 3中的行为是"固定的":打印自引用元组会给出省略号截断的形式.)

Bli*_*lin 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
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

这意味着只有当对象的类型没有tp_print函数时才会调用__str____repr__调用它.tupleobject有一个.

如果你想了解CPython的内部,最好的方法是阅读源代码.我推荐一系列关于python内部的教程,它解释了你必须知道的一切,以完全理解python dis函数的输出.