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 中是“固定的”:打印自引用元组会给出省略号截断的形式。)

4

1 回答 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 函数时才会调用 or __str____repr__并且 tupleobject 有一个

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

于 2013-12-21T10:55:29.140 回答