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'