deff():print(c) a =1 b =3defg():print(a+b) c=2defh():print(a+b+c)
example.py 被编译后再进行反汇编,我们能够得到以下字节码:
代码清单5.10: example.py 模块代码块对应的字节码
10 LOAD_CONST 0 (<code object f at 0x000002D7ADE8E540, file "example.py", line 1>)2 LOAD_CONST 1 ('f')4 MAKE_FUNCTION 06 STORE_NAME 0 (f)8 LOAD_CONST 2 (None)10 RETURN_VALUE
字节偏移量为0的指令将加载一个代码对象,该对象存储为名称 f 使用 MAKE_FUNCTION 指令的函数定义。 清单5.11显示了此代码对象的内容。
代码清单5.11: example.py 模块代码对象的属性
co_argcount:0co_cellvars: ()co_code:b'd\x00d\x01\x84\x00Z\x00d\x02S\x00'co_consts: (<code object f at 0x000002D7ADE8E540, file "example.py", line 1>,'f',None)co_filename: example.pyco_firstlineno:1co_flags:64co_freevars: ()co_kwonlyargcount:0co_lnotab:b''co_name:<module>co_names: ('f',)co_nlocals:0co_stacksize:2co_varnames: ()
就像预期的那样,模块代码对象与参数相关的字段(co_argcount,co_kwonlyargcount)全为0。 co_code 字段包含字节码指令,如清单5.10所示。 co_consts 字段是一个有趣的字段。 字段中的常量是代码对象,名称为 f 和 None。 其中的代码对象是 example.py 中函数 f 对应的代码对象,值 “f” 是函数的名称,“None”是函数的返回值。回想一下,python 编译器会向没有一个没有返回值的代码对象添加 return None 语句。
请注意,在模块编译期间实际上并未创建函数对象。 我们所拥有的只是代码对象,函数实际上是在代码对象执行期间创建的,如清单5.10所示。如果检查一下函数 f 的代码对象的属性,会发现,实际上它也由其他代码对象(内部嵌套函数 g 对应的代码对象)组成,如代码清单5.12所示。
代码清单5.11: 函数 f 对应的代码对象的属性
co_argcount:0co_cellvars: ('a','b')co_code: b't\x00t\x01\x83\x01\x01\x00d\x01\x89\x00d\x02\x89\x01\x87\x00\x87\x01f\x02d\x03d\x04\x84\x08}\x00d\x00S\x00'
co_consts: (None,1,3,<code object g at 0x000002D7ADE8E6F0, file "example.py", line 5>,'f.<locals>.g')co_filename: example.pyco_firstlineno:1co_flags:3co_freevars: ()co_kwonlyargcount:0co_lnotab:b'\x00\x01\x08\x01\x04\x01\x04\x01'co_name: fco_names: ('print','c')co_nlocals:1co_stacksize:3co_varnames: ('g',)
typedefstruct { PyObject_HEADint co_argcount; /* #arguments, except *args */int co_kwonlyargcount; /* #keyword only arguments */int co_nlocals; /* #local variables */int co_stacksize; /* #entries needed for evaluation stack */int co_flags; /* CO_..., see below */int co_firstlineno; /* first source line number */ PyObject *co_code; /* instruction opcodes */ PyObject *co_consts; /* list (constants used) */ PyObject *co_names; /* list of strings (names used) */ PyObject *co_varnames; /* tuple of strings (local variable names) */ PyObject *co_freevars; /* tuple of strings (free variable names) */ PyObject *co_cellvars; /* tuple of strings (cell variable names) */ /* The rest aren't used in either hash or comparisons, except for co_name, used in both. This is done to preserve the name and line number for tracebacks and debuggers; otherwise, constant de-duplication would collapse identical functions/lambdas defined on different lines. */Py_ssize_t*co_cell2arg; /* Maps cell vars which are arguments. */ PyObject *co_filename; /* unicode (where it was loaded from) */ PyObject *co_name; /* unicode (name, for reference) */ PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See Objects/lnotab_notes.txt for details. */void*co_zombieframe; /* for optimization only (see frameobject.c) */ PyObject *co_weakreflist; /* to support weakrefs to code objects */ /* Scratch space for extra data relating to the code object. Type is a void* to keep the format private in codeobject.c to force people to go through the proper APIs. */void*co_extra;} PyCodeObject;