Python源码学习:Python函数浅析-无参函数
2018-03-31 12:55
746 查看
Python源码分析
本文环境python2.5系列 参考书籍<<Python源码剖析>>
本文会大致分析一下Python中的函数机制。在Python中,函数是一个比较重要的类型,在实现过程中主要参考了操作系统中的函数调用过程,把每个函数模拟成一段待执行的代码,在运行过程中调用,每一段执行的PyCodeObject都被包装在frame中,等待被调用执行,然后调用虚拟机调用执行,这个过程就是一个嵌套执行的过程。
分析
typedef struct { PyObject_HEAD PyObject *func_code; /* A code object */ // PyCodeObject对象 PyObject *func_globals; /* A dictionary (other mappings won't do) */ //对应的函数全局变量 PyObject *func_defaults; /* NULL or a tuple */ //函数的默认参数 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 */ /* 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;
由定义可知,PyFunctionObject也是一个PyObject,其中func_code是Python编译器在编译的时候就生成的对象,将Python的执行代码编译成了一个PyCodeObject对象,PyFunctionObject是Python解释器在执行字节码时,根据PyCodeObject代码生成的,并且包含函数执行时相应的全局变量。
先分析如下例子
def f(): print("hello world") f()
对应编译后的字节码为
1 0 LOAD_CONST 0 (<code object f at 0x1044fd630, file "test1.py", line 1>) 3 MAKE_FUNCTION 0 6 STORE_NAME 0 (f) 4 9 LOAD_NAME 0 (f) 12 CALL_FUNCTION 0 15 POP_TOP 16 LOAD_CONST 1 (None) 19 RETURN_VALUE
并且可以查看一下,code object f的结果为
2 0 LOAD_CONST 1 ('hello world') 3 PRI e1a4 NT_ITEM 4 PRINT_NEWLINE 5 LOAD_CONST 0 (None) 8 RETURN_VALUE
通过两段字节码的分析可知,该代码里面包含了两个PyCodeObject一个是运行的本身,另一个测试f对应的PyCodeObject。先分析本身的字节码执行,在LOAD_CONST对应的f的PyCodeObject后,直接调用了MAKE_FUNCTION,我们查看一些具体的执行流程。
v = POP(); /* code object */ //获取code x = PyFunction_New(v, f->f_globals); //调用新建函数方法,并将当前执行的全局变量传入 Py_DECREF(v); /* XXX Maybe this should be a separate opcode? */ if (x != NULL && oparg > 0) { v = PyTuple_New(oparg); if (v == NULL) { Py_DECREF(x); x = NULL; break; } while (--oparg >= 0) { w = POP(); PyTuple_SET_ITEM(v, oparg, w); // 根据传入参数的个数,将传入参数设置到tuple中 } err = PyFunction_SetDefaults(x, v); // 处理函数的默认参数 Py_DECREF(v); } PUSH(x); break;
由此我们进一步分析PyFunction_New代码
PyObject * PyFunction_New(PyObject *code, PyObject *globals) { PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); // 申请函数对象的空间 static PyObject *__name__ = 0; if (op != NULL) { PyObject *doc; PyObject *consts; PyObject *module; op->func_weakreflist = NULL; Py_INCREF(code); op->func_code = code; // 设置函数对应的PyCodeObject Py_INCREF(globals); op->func_globals = globals; // 设置函数的全局变量 op->func_name = ((PyCodeObject *)code)->co_name; // 设置函数的名称 Py_INCREF(op->func_name); op->func_defaults = NULL; /* No default arguments */ // 设置函数的默认参数 op->func_closure = NULL; consts = ((PyCodeObject *)code)->co_consts; // code中的常量 if (PyTuple_Size(consts) >= 1) { // 获取函数的doc doc = PyTuple_GetItem(consts, 0); if (!PyString_Check(doc) && !PyUnicode_Check(doc)) doc = Py_None; } else doc = Py_None; Py_INCREF(doc); op->func_doc = doc; op->func_dict = NULL; op->func_module = NULL; /* __module__: If module name is in globals, use it. Otherwise, use None. */ if (!__name__) { __name__ = PyString_InternFromString("__name__"); if (!__name__) { Py_DECREF(op); return NULL; } } module = PyDict_GetItem(globals, __name__); if (module) { Py_INCREF(module); op->func_module = module; } } else return NULL; _PyObject_GC_TRACK(op); return (PyObject *)op; }
主要工作就是完成申请PyFunctionObject的空间大小,设置各个参数值。
至此,一个PyFunctionObject就完成创建。
接着就是LOAD_NAME f,此时就加载刚刚创建好的函数对象,然后调用CALL_FUNCTION,我们继续查看;
case CALL_FUNCTION: { PyObject **sp; PCALL(PCALL_ALL); sp = stack_pointer; #ifdef WITH_TSC x = call_function(&sp, oparg, &intr0, &intr1); #else x = call_function(&sp, oparg); #endif stack_pointer = sp; PUSH(x); if (x != NULL) continue; break; }
此时,将函数传入call_function中,
static PyObject * call_function(PyObject ***pp_stack, int oparg #ifdef WITH_TSC , uint64* pintr0, uint64* pintr1 #endif ) { int na = oparg & 0xff; // 获取输入参数的个数 int nk = (oparg>>8) & 0xff; // 获取位置参数的个数 int n = na + 2 * nk; // 总大小,由于一个位置参数由key和value组成,所有乘2 PyObject **pfunc = (*pp_stack) - n - 1; // 获取函数对象 PyObject *func = *pfunc; PyObject *x, *w; /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. */ if (PyCFunction_Check(func) && nk == 0) { // 检查func的类型,是否为cfunc ... } else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { // 检查func是否是类访问的方法 /* optimize access to bound methods */ PyObject *self = PyMethod_GET_SELF(func); PCALL(PCALL_METHOD); PCALL(PCALL_BOUND_METHOD); Py_INCREF(self); func = PyMethod_GET_FUNCTION(func); Py_INCREF(func); Py_DECREF(*pfunc); *pfunc = self; na++; n++; } else Py_INCREF(func); READ_TIMESTAMP(*pintr0); if (PyFunction_Check(func)) // 检查是否是函数类型 x = fast_function(func, pp_stack, n, na, nk); // 处理快速方法 else x = do_call(func, pp_stack, na, nk); READ_TIMESTAMP(*pintr1); Py_DECREF(func); } /* Clear the stack of the function object. Also removes the arguments in case they weren't consumed already (fast_function() and err_args() leave them on the stack). */ while ((*pp_stack) > pfunc) { w = EXT_POP(*pp_stack); Py_DECREF(w); PCALL(PCALL_POP); } return x; }
主要是先判断函数的类型,然后在根据函数的类型进行调用,在本例中,会调用fast_function;
static PyObject * fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); // 获取函数的对应的字节码 PyObject *globals = PyFunction_GET_GLOBALS(func); // 获取函数的执行时的全局变量 PyObject *argdefs = PyFunction_GET_DEFAULTS(func); // 获取函数的默认参数 PyObject **d = NULL; int nd = 0; PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); if (argdefs == NULL && co->co_argcount == n && nk==0 && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { // 一般函数的执行过程 PyFrameObject *f; // 调用函数对应的帧 PyObject *retval = NULL; // 参数执行完成后的返回结果 PyThreadState *tstate = PyThreadState_GET(); // 获取当前线程的状态 PyObject **fastlocals, **stack; int i; PCALL(PCALL_FASTER_FUNCTION); assert(globals != NULL); /* XXX Perhaps we should create a specialized PyFrame_New() that doesn't take locals, but does take builtins without sanity checking them. */ assert(tstate != NULL); f = PyFrame_New(tstate, co, globals, NULL); // 生成一个新的帧对象 if (f == NULL) return NULL; fastlocals = f->f_localsplus; // 本地变量 stack = (*pp_stack) - n; for (i = 0; i < n; i++) { Py_INCREF(*stack); fastlocals[i] = *stack++; } retval = PyEval_EvalFrameEx(f,0); // 调用解释器继续执行函数对应的字节码 ++tstate->recursion_depth; Py_DECREF(f); --tstate->recursion_depth; return retval; } if (argdefs != NULL) { d = &PyTuple_GET_ITEM(argdefs, 0); nd = ((PyTupleObject *)argdefs)->ob_size; } return PyEval_EvalCodeEx(co, globals, (PyObject *)NULL, (*pp_stack)-n, na, (*pp_stack)-2*nk, nk, d, nd, PyFunction_GET_CLOSURE(func)); }
此时,调用fast_function就又调用解释器函数执行字节码,然后执行完成将结果返回,此时解释器执行的字节码为code f的字节码。
2 0 LOAD_CONST 1 ('hello world') 3 PRINT_ITEM 4 PRINT_NEWLINE 5 LOAD_CONST 0 (None) 8 RETURN_VALUE
该段字节码执行的源码分析,有兴趣可自行查看,执行完成后就是打印hello world,然后返回None值,然后通过RETURN_VALUE;
case RETURN_VALUE: retval = POP(); // 弹出返回结果 why = WHY_RETURN; goto fast_block_end; // 跳转到fast_block_end
fast_block_end,该处代码就是判断当前执行流程中,是否有错误或者执行完成已经有返回结果了就中断循环,然后就执行到
/* pop frame */ exit_eval_frame: Py_LeaveRecursiveCall(); tstate->frame = f->f_back; // 将当前执行的帧设置为调用的帧 return retval; // 返回调用执行的结果
至此,执行完成结果便会返回。
本次的无参函数的调用就分析完成。
相关文章推荐
- Python源码学习:Python函数浅析-有参函数
- 深入学习Django源码基础2 - python多态技巧-运行时判断函数
- Python函数世界杯皇冠体育足球源码下载学习——递归
- python学习笔记之函数及其高级特性
- python学习——Python join()函数
- 第二周的Python学习---函数
- 【11】Python函数学习(上)
- Python基础学习之函数(1)
- python学习之函数
- Python学习——enumerate()函数的用法
- Python学习之路-变量、字符串、函数
- Python学习函数,“利用函数修改元组的坑”
- Python学习六:函数
- python函数学习笔记
- 深入学习Django源码基础1 - 深刻理解Python中的元类(metaclass)
- 学习python(四) 函数
- Python学习(二):入门篇:python中流程控制与函数编写
- Python新手学习基础之函数-全局变量和局部变量
- python学习笔记12(函数三): 参数类型、递归、lambda函数
- Python学习笔记总结(二):函数和模块