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.该
字符串转换的规则是:
5.2.9.字符串转换
字符串转换是一个反向(又称向后)引号括起来的表达式列表:
Run Code Online (Sandbox Code Playgroud)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
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函数的输出.