Python 是否在模块中隐式 return None?

Does Python implicitly return None in modules?

我在 Python 字节码和 dis 模块一起玩时,我注意到一些关于为模块生成的字节码的事情。给定一个名为 mod.py:

的小测试模块
if __name__ == "__main__":
    print("Hello World")

我用 compile() 函数为它创建了一个代码对象:

>>> cobj = compile(open("mod.py").read(), "mod", "exec")

然后,反汇编代码对象查看字节码:

>>> dis.dis(cobj)
  1           0 LOAD_NAME                0 (__name__)
              3 LOAD_CONST               0 ('__main__')
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_FALSE       22

  2          12 LOAD_NAME                1 (print)
             15 LOAD_CONST               1 ('Hello World')
             18 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             21 POP_TOP
        >>   22 LOAD_CONST               2 (None)
             25 RETURN_VALUE

让我印象深刻的是最后两个命令 LOAD_CONST and RETURN_VALUE,如果我的理解是正确的,将常量 None 放在 值堆栈 上,因此return它。

隐式执行模块 return None 如果是,为什么?

是的,简而言之,模块隐式执行 return None 以便 ceval.c 内的大评估循环能够检测当前帧何时完成并终止。

有趣的是,即使在从终端解释完全空的 python 文件时,您也可以看到这一点:

jim@lpt> touch file.py
jim@lpt> python -m dis file.py
1           0 LOAD_CONST               0 (None)
            3 RETURN_VALUE    

此行为不仅限于模块,而是 for anything that can form a code block;这包括 class 定义:

>>> from dis import dis
>>> co = compile("class mycls: pass", filename = "stdin", mode = "exec")
>>> dis(co.co_consts[0])  # co_consts[0] contains class definition
  1           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)
              6 LOAD_CONST               0 ('mycls')
              9 STORE_NAME               2 (__qualname__)
             12 LOAD_CONST               1 (None)
             15 RETURN_VALUE

函数体

>>> def foo(): pass

>>> dis(foo)
  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE

也就是RETURN_VALUE字节码命令terminates the current block that was executing和returns(LOAD_CONST)值在最上面堆栈到被调用者(如果存在的话)。

唯一需要注意的是,指定自定义 return 值仅允许用于函数,在 class 定义和模块中,return 值没有太多一个目的,因此尝试指定一个目的会导致适当的 SyntaxError.


对于对此源代码感兴趣的人,您可以通过逐步执行 compile.c 中定义的函数,从 [=19] 中的文档中找到它=]:

 * The primary entry point is PyAST_Compile(), which returns a
 * PyCodeObject.  The compiler makes several passes to build the code
 * object:
 *   1. Checks for future statements.  See future.c
 *   2. Builds a symbol table.  See symtable.c.
 *   3. Generate code for basic blocks.  See compiler_mod() in this file.
 *   4. Assemble the basic blocks into final code.  See assemble() in
 *      this file.
 *   5. Optimize the byte code (peephole optimizations).  See peephole.c

相关步骤是3.4.compiler_mod and assemble函数