函数对象的数据分为两个主要部分。对于由同一函数定义创建的所有函数而言,相同的部分存储在函数的代码对象中,而即使在从同一函数定义创建的函数之间也可以更改的部分存储在函数对象中。在
函数最有趣的部分可能是它的字节码。这是一个核心数据结构,它说明了如何执行一个函数。它以bytestring的形式存储在函数的code对象中,您可以直接检查它:>>> def fib(i):
... x, y = 0, 1
... for _ in range(i):
... x, y = y, x+y
... return x
...
>>> fib.__code__.co_code
b'd\x03\\\x02}\x01}\x02x\x1et\x00|\x00\x83\x01D\x00]\x12}\x03|\x02|\x01|\x02\x17\x00\x02\x00}\x01}\x02q\x1
2W\x00|\x01S\x00'
…但它不是为人类可读而设计的。在
有了足够的Python字节码实现细节的知识,您可以自己解析它,但是描述所有这些都需要太长时间。相反,我们将使用^{}模块为我们反汇编字节码:
^{pr2}$
这里的输出中有很多列,但我们最感兴趣的是那个有全大写字母和右边列的那个。在
ALL\u CAPS列显示函数的字节码指令。例如,LOAD_CONST加载一个常量值,BINARY_ADD是用+添加两个对象的指令。下一列(带数字)是字节码参数。例如,LOAD_CONST 3表示加载代码对象常量中索引3处的常量。这些总是整数,它们与字节码指令一起打包到字节码字符串中。最后一列主要提供了字节码参数的可读解释,例如,LOAD_CONST 3中的3对应于常量(0, 1),或者STORE_FAST 1中的1对应于局部变量{}。此列中的信息实际上并非来自字节码字符串;它是通过检查代码对象的其他部分来解决的。在
函数对象的其余数据主要是解析字节码参数所需的数据,如函数的闭包或其全局变量dict,以及那些只存在于方便内省的内容,如函数的__name__。在typedef struct {
PyObject_HEAD
PyObject *func_code; /* A code object, the __code__ attribute */
PyObject *func_globals; /* A dictionary (other mappings won't do) */
PyObject *func_defaults; /* NULL or a tuple */
PyObject *func_kwdefaults; /* NULL or a dict */
PyObject *func_closure; /* NULL or a tuple of cell objects */
PyObject *func_doc; /* The __doc__ attribute, can be anything */
PyObject *func_name; /* The __name__ attribute, a string object */
PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
PyObject *func_weakreflist; /* List of weak references */
PyObject *func_module; /* The __module__ attribute, can be anything */
PyObject *func_annotations; /* Annotations, a dict or NULL */
PyObject *func_qualname; /* The qualified name */
/* Invariant:
* func_closure contains the bindings for func_code->co_freevars, so
* PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
* (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
*/
} PyFunctionObject;
我们可以看到代码对象,然后全局变量dict
默认参数值
仅默认关键字值
功能的闭合细胞
文档字符串
名字
__dict__
函数的弱引用列表
__module__
注释,以及
__qualname__,完全限定名
在PyObject_HEAD宏中,还有类型指针和一些refcount/GC元数据。在
我们不必直接使用C来检查大部分内容—我们可以查看dir并过滤掉非实例属性,因为大多数数据在Python级别都是可用的—但是结构定义提供了一个很好的、注释过的、整洁的列表。在
你也可以检查code object struct definition,但是如果你还不熟悉代码对象,内容就不那么清晰了,所以我不打算把它嵌入文章中。我只解释代码对象。在
code对象的核心组件是Python字节码指令和参数的bytestring。我们之前检查过其中一个。此外,code对象还包含诸如函数所引用的常量元组之类的内容,以及许多其他内部元数据,以确定如何实际执行每条指令。不是所有的元数据(其中一些来自函数对象),而是很多元数据。其中一些,比如常量元组,是相当容易理解的,而另一些,比如co_flags(一组内部标志)或{}(用于临时值的堆栈大小)更为深奥。在