学过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