学过C的同学都知道,在X86平台上调用函数时,系统会在运行时栈中创建新的栈帧用于函数执行,其中包括函数参数入栈,运行指令,调用现场恢复(取决于函数调用方式),Python中同样也是这个过程。
CPython中PyFrameObject对象就是一个栈帧的模拟,所以Python的虚拟机在执行函数调用时会动态的创建新的PyFrameObject对象,随着函数调用链的增长,这些object之间也会连接成一条PyFrameObject对象链。
在Python中,任何东西都是一个对象,函数也不列外。函数的这种抽象机制是通过一个Python对象:PyFunctionObject来实现的,一下是Python 2.6.5中funcobject的结构体,位于Include的funcobject.h文件夹中:
1 typedef struct {
2 PyObject_HEAD
3 PyObject *func_code; /* A code object */
4 PyObject *func_globals; /* A dictionary (other mappings won't do) */
5 PyObject *func_defaults; /* NULL or a tuple */
6 PyObject *func_closure; /* NULL or a tuple of cell objects */
7 PyObject *func_doc; /* The __doc__ attribute, can be anything */
8 PyObject *func_name; /* The __name__ attribute, a string object */
9 PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
10 PyObject *func_weakreflist; /* List of weak references */
11 PyObject *func_module; /* The __module__ attribute, can be anything */
12
13 /* Invariant:
14 * func_closure contains the bindings for func_code->co_freevars, so
15 * PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
16 * (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
17 */
18 } PyFunctionObject;
Python中油两个对象和函数有关,PyCodeObject和PyFunctionObject。
PyCodeObject定义如下(Include/code.h):
1 /* Bytecode object */
2 typedef struct {
3 PyObject_HEAD
4 int co_argcount; /* #arguments, except *args */
5 int co_nlocals; /* #local variables */
6 int co_stacksize; /* #entries needed for evaluation stack */
7 int co_flags; /* CO_..., see below */
8 PyObject *co_code; /* instruction opcodes */
9 PyObject *co_consts; /* list (constants used) */
10 PyObject *co_names; /* list of strings (names used) */
11 PyObject *co_varnames; /* tuple of strings (local variable names) */
12 PyObject *co_freevars; /* tuple of strings (free variable names) */
13 PyObject *co_cellvars; /* tuple of strings (cell variable names) */
14 /* The rest doesn't count for hash/cmp */
15 PyObject *co_filename; /* string (where it was loaded from) */
16 PyObject *co_name; /* string (name, for reference) */
17 int co_firstlineno; /* first source line number */
18 PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */
19 void *co_zombieframe; /* for optimization only (see frameobject.c) */
20 } PyCodeObject;
PyCodeObject对象是一段Python源代码的静态表示。Python对源代码经过编译后,对一个代码段会产生一个且只有一个PyCodeObject,这个对象中包含了代码段的一些静态信息,所谓静态信息是指,比如某个代码段有a=1这样的表达式,那么符号a和值1以及他们之间的联系就是一种静态的信息,这些信息会分别存储在PyCodeObject的常量表co_consts,符号表co_names以及字节码序列co_code中,这些信息是编译时就可以得到的,因此PyCodeObject是编译时的结果。请看下面的Python代码:
1 what = 3
2 def test(x):
3 i = None
4 t = x
5 a = 1
6 b = 2
7 c = 3
8 d = x + 3
9 print what
10
11
12
13 print "co_consts:",test.func_code.co_consts
14 print "co_names:",test.func_code.co_names
15 #print test.func_code.co_code
16 print "co_varnames:",test.func_code.co_varnames
17 print "co_freevars:",test.func_code.co_freevars
运行结果:
> "D:\Python26\python.exe" -u "F:\Documents\tes.py"
co_consts: (None, 1, 2, 3)
co_names: ('None', 'what')
co_varnames: ('x', 'i', 't', 'a', 'b', 'c', 'd')
co_freevars: ()
不同于PyCodeObject,PyFunctionObject对象是Python代码在运行def语句时动态产生的。在PyFunctionObject中当然会包括这个函数的静态信息,这些信息储存在func_code中,如上例所示。除此之外PyFunctionObject对象中还包含了一些函数在执行时必须的动态信息,上下文信息,比如func_globals,就是在函数执行时关联的全局作用域。全局作用域中的符号和值得对应关系必须在运行时才能确定,所以这部分必须在运行时动态创建。
对于一段Python代码,其对应的PyCodeObject对象只有一个,而代码所对应的PyFunctionObject对象却可能有很多个,比如一个函数的多次调用。
函数对象的创建
考察下面这段代码:
def test():
print "Function"
test()
以及对应的字节码:
2 0 LOAD_CONST 0 (<code object test at 00B38B18, file "tes.py", line 2>)
3 MAKE_FUNCTION 0
6 STORE_NAME 0 (test)
6 9 LOAD_NAME 0 (test)
12 CALL_FUNCTION 0
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
None
可以注意到,正如上文所述,code object为编译时的静态信息(LOAD_CONST)。
函数字节码:
import dis
def test():
print "Function"
print dis.dis(test)
test()
=========输出==========
3 0 LOAD_CONST 1 ('Function')
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
None
Function
观察上述字节码可以发现,在我们定义test函数的时候,并没有出现print ”Function“ 字节码,阅读Objects/funcobject.c的PyFunction_New函数,我们可以很容易发现,Python中,函数的申明与实现其实是分离的。Python虚拟机在执行def语句时,会动态地创建一个函数,即一个PyFunctionObject,在这个过程中,MAKE_FUNCTION指令是一个关键。
TO BE CONTINUED