CPython';s解释器知道打印最后一个表达式的结果吗?
我一直在挖掘源代码,以找出结果是在哪一点打印出来的。例如:CPython';s解释器知道打印最后一个表达式的结果吗?,python,cpython,python-internals,Python,Cpython,Python Internals,我一直在挖掘源代码,以找出结果是在哪一点打印出来的。例如: >>> x = 1 >>> x + 2 3 上述两项声明的汇编目的是: 1 0 LOAD_CONST 0 (1) 3 STORE_NAME 0 (x) 6 LOAD_CONST 1 (None) 9 RETUR
>>> x = 1
>>> x + 2
3
上述两项声明的汇编目的是:
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (x)
6 LOAD_CONST 1 (None)
9 RETURN_VALUE
及
第一条语句不打印任何内容,因为None
是返回值。第二个返回加法的结果
CPython的交互循环调用每个输入。然后在虚拟机中调用run_mod()
。PyRun\u InteractiveOneObjectEx()
获取的返回Python对象就是
到目前为止,所有这些都是我所期望的。但是返回的值似乎是!这是什么时候由REPL打印的
另外,我可以看到交互模式确实改变了标记器;它使用
sys.ps1
提示符(“>>>”
默认情况下)启动。我在pythonrun.c
中检查了类似的更改,但运气不好。您正在显示字节码的分解,该字节码是由函数中的代码生成的。这不是交互式代码的编译方式:它使用一种特殊的“单一”模式(如果您在Python代码中执行等效操作,则第三个参数为compile()
)。在此模式下,丢弃每个表达式值的POP_-TOP
操作码变为PRINT_-EXPR
。x=1
不打印任何内容的原因是,语句在堆栈上没有留下任何需要弹出的内容,因此此转换不适用。正确!对于子孙后代,这里有一个更深入的了解正在发生什么
上述反汇编显示了eval
模式的结果:
>>> list(compile('x + 2', '<stdin>', 'eval').co_code)
[101, 0, 0, 100, 0, 0, 23, 83]
操作码后面的两个数字表示一个16位操作数,尽管这里只需要第一个字节。因此,这对应于:
LOAD_NAME 0
LOAD_CONST 0
BINARY_ADD
RETURN_VALUE
这些操作数分别是编译代码对象的变量名和常量池的索引
>>> c = compile('x + 2', '<stdin>', 'eval')
>>> c.co_names
('x',)
>>> c.co_consts
(2,)
结果被打印(通过),并且None
成为实际返回值
因此,打印是由代码生成阶段引入的,而不是由VM引入的
LOAD_NAME 0
LOAD_CONST 0
BINARY_ADD
RETURN_VALUE
>>> c = compile('x + 2', '<stdin>', 'eval')
>>> c.co_names
('x',)
>>> c.co_consts
(2,)
>>> list(compile('x + 2', '<stdin>', 'exec').co_code)
[101, 0, 0, 100, 0, 0, 23, 1, 100, 1, 0, 83]
>>> dis.opname[1]
'POP_TOP'
>>> list(compile('x + 2', '<stdin>', 'single').co_code)
[101, 0, 0, 100, 0, 0, 23, 70, 100, 1, 0, 83]
>>> dis.opname[70]
'PRINT_EXPR'