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 "", line 1, in MemoryError: stack overflow >>> print repr(outer) ((...),) >>> print str(outer) ((...),) >>> print unicode(outer) ((...),)
到底是print
做什么的?为了尝试自己回答这个问题,我提到了语言参考:
6.6.该
字符串转换的规则是:
5.2.9.字符串转换
字符串转换是一个反向(又称向后)引号括起来的表达式列表:
string_conversion ::= "`" expression_list "`"
但封闭outer
在后面的引号与调用repr()
和朋友的结果相同.没有骰子.那么print
幕后真的在做什么呢?
(有趣的是,Python 3中的行为是"固定的":打印自引用元组会给出省略号截断的形式.)
你可以通过反汇编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有一个.
如果你想了解CPython的内部,最好的方法是阅读源代码.我推荐一系列关于python内部的教程,它解释了你必须知道的一切,以完全理解python dis函数的输出.