赞
踩
在 python 中函数也是一个对象
typedef struct {
PyObject_HEAD
PyObject *func_code; /* 函数编译之后的 PyCodeObject, 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, 函数编译之后,代码段的第一个常量 */
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;
PyCodeObject 是一个静态代码块的描述,说静态是应该在编译的时候就可以知道该
代码块有哪些内容。比如常量表,符号表,局部变量等等,我们一般都针对的是
PyCodeObject。
而 PyFunctionObject 是动态的,这个对象是定义 def 的函数之后,每个一个函数
都会对应一个 PyFunctionObject,其中 func_code 保存的就是该函数的代码相关
部分,除此之外,还有 globals, default 等等,一般是动态决定的。 还要需要
注意的是,对于一个函数 f,在运行过程中,可能在多处调用,就会创建多个
PyFunctionObject 对象,而每个对象的 func_code 都指向同一 PyCodeObject。
所以,对于一个 PyFunctionObject 它既包含静态部分,如 func_code, 也包含
动态部分,比如函数参数。
#!/usr/bin/env python
# encoding: utf-8
def test():
a = 1
test()
编译之后
magic b'330d0d0a'
moddate b'c075345b' (Thu Jun 28 13:44:32 2018)
files sz 604
code
argcount 0
nlocals 0
stacksize 2
flags 0040
code b'6400640184005a0065008300010064025300'
4 0 LOAD_CONST 0 (<code object test at 0x10ca48ae0, file "/tmp/a.py", line 4>)
2 LOAD_CONST 1 ('test')
4 MAKE_FUNCTION 0 //创建 PyFunctionObject 并入栈
6 STORE_NAME 0 (test) //将 PyFunctionObject 保存到 test
7 8 LOAD_NAME 0 (test)
10 CALL_FUNCTION 0
12 POP_TOP
14 LOAD_CONST 2 (None)
16 RETURN_VALUE
consts
code
argcount 0
nlocals 1
stacksize 1
flags 0043
code b'64017d0064005300'
5 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (a)
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
consts
None
1
names ()
varnames ('a',)
freevars ()
cellvars ()
filename '/tmp/a.py'
name 'test'
firstlineno 4
lnotab b'0001'
'test'
None
names ('test',)
varnames ()
freevars ()
cellvars ()
filename '/tmp/a.py'
name '<module>'
firstlineno 4
lnotab b'0803'
由编译可知产生两个代码段,一个是 a.py 对应的 PyCodeObject,一个是函数 test 对应的 PyCodeObject。而 test 对应的 PyCodeObject 是一个常量。
PyObject *
PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
{
PyFunctionObject *op;
PyObject *doc, *consts, *module;
static PyObject *__name__ = NULL;
if (__name__ == NULL) {
__name__ = PyUnicode_InternFromString("__name__");
if (__name__ == NULL)
return NULL;
}
op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);
if (op == NULL)
return NULL;
op->func_weakreflist = NULL;
Py_INCREF(code);
op->func_code = code;
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_kwdefaults = NULL; /* No keyword only defaults */
op->func_closure = NULL;
consts = ((PyCodeObject *)code)->co_consts;
if (PyTuple_Size(consts) >= 1) {
doc = PyTuple_GetItem(consts, 0);
if (!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;
op->func_annotations = NULL;
/* __module__: If module name is in globals, use it.
Otherwise, use None. */
module = PyDict_GetItem(globals, __name__);
if (module) {
Py_INCREF(module);
op->func_module = module;
}
if (qualname)
op->func_qualname = qualname;
else
op->func_qualname = op->func_name;
Py_INCREF(op->func_qualname);
_PyObject_GC_TRACK(op);
return (PyObject *)op;
}
TARGET(MAKE_FUNCTION) {
PyObject *qualname = POP();
PyObject *codeobj = POP();
PyFunctionObject *func = (PyFunctionObject *)
PyFunction_NewWithQualName(codeobj, f->f_globals, qualname);
Py_DECREF(codeobj);
Py_DECREF(qualname);
if (func == NULL) {
goto error;
}
if (oparg & 0x08) {
assert(PyTuple_CheckExact(TOP()));
func ->func_closure = POP();
}
if (oparg & 0x04) {
assert(PyDict_CheckExact(TOP()));
func->func_annotations = POP();
}
if (oparg & 0x02) {
assert(PyDict_CheckExact(TOP()));
func->func_kwdefaults = POP();
}
if (oparg & 0x01) {
assert(PyTuple_CheckExact(TOP()));
func->func_defaults = POP();
}
//当前函数对应的 PyFunctionObject 入栈
PUSH((PyObject *)func);
DISPATCH();
}
创建一个 PyFunctionObject 对象,其 globals, 都来自外部的 PyFrameObject
static PyObject *
call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
{
//执行函数,从栈指针定位到函数
PyObject **pfunc = (*pp_stack) - oparg - 1;
PyObject *func = *pfunc;
PyObject *x, *w;
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
//nargs 参数个数
Py_ssize_t nargs = oparg - nkwargs;
PyObject **stack;
/* Always dispatch PyCFunction first, because these are
presumed to be the most frequent callable object.
*/
//C 函数
if (PyCFunction_Check(func)) {
PyThreadState *tstate = PyThreadState_GET();
PCALL(PCALL_CFUNCTION);
stack = (*pp_stack) - nargs - nkwargs;
C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
}
else {
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
/* 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_SETREF(*pfunc, self);
nargs++;
}
else {
Py_INCREF(func);
}
stack = (*pp_stack) - nargs - nkwargs;
if (PyFunction_Check(func)) {
x = fast_function(func, stack, nargs, kwnames);
}
else {
x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
}
Py_DECREF(func);
}
assert((x != NULL) ^ (PyErr_Occurred() != NULL));
/* 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;
}
TARGET(CALL_FUNCTION) {
PyObject **sp, *res;
PCALL(PCALL_ALL);
sp = stack_pointer;
res = call_function(&sp, oparg, NULL);
stack_pointer = sp;
PUSH(res);
if (res == NULL) {
goto error;
}
DISPATCH();
}
//Object/methodobject.c
PyObject *
_PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack,
Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *kwdict, *result;
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
assert(PyCFunction_Check(func));
assert(nargs >= 0);
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
/* kwnames must only contains str strings, no subclass, and all keys must
be unique */
if (nkwargs > 0) {
kwdict = _PyStack_AsDict(stack + nargs, kwnames);
if (kwdict == NULL) {
return NULL;
}
}
else {
kwdict = NULL;
}
result = _PyCFunction_FastCallDict(func, stack, nargs, kwdict);
Py_XDECREF(kwdict);
return result;
}
#define PyCFunction_GET_FUNCTION(func) \
(((PyCFunctionObject *)func) -> m_ml -> ml_meth)
PyObject *
_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
PyObject *kwargs)
{
PyCFunctionObject *func = (PyCFunctionObject*)func_obj;
PyCFunction meth = PyCFunction_GET_FUNCTION(func);
PyObject *self = PyCFunction_GET_SELF(func);
PyObject *result;
int flags;
assert(PyCFunction_Check(func));
assert(func != NULL);
assert(nargs >= 0);
assert(nargs == 0 || args != NULL);
assert(kwargs == NULL || PyDict_Check(kwargs));
/* _PyCFunction_FastCallDict() must not be called with an exception set,
because it may clear it (directly or indirectly) and so the
caller loses its exception */
assert(!PyErr_Occurred());
flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
switch (flags)
{
case METH_NOARGS:
if (kwargs != NULL && PyDict_Size(kwargs) != 0) {
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
func->m_ml->ml_name);
return NULL;
}
if (nargs != 0) {
PyErr_Format(PyExc_TypeError,
"%.200s() takes no arguments (%zd given)",
func->m_ml->ml_name, nargs);
return NULL;
}
result = (*meth) (self, NULL);
break;
case METH_O:
if (kwargs != NULL && PyDict_Size(kwargs) != 0) {
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
func->m_ml->ml_name);
return NULL;
}
if (nargs != 1) {
PyErr_Format(PyExc_TypeError,
"%.200s() takes exactly one argument (%zd given)",
func->m_ml->ml_name, nargs);
return NULL;
}
result = (*meth) (self, args[0]);
break;
case METH_VARARGS:
case METH_VARARGS | METH_KEYWORDS:
{
/* Slow-path: create a temporary tuple */
PyObject *tuple;
if (!(flags & METH_KEYWORDS) && kwargs != NULL && PyDict_Size(kwargs) != 0) {
PyErr_Format(PyExc_TypeError,
"%.200s() takes no keyword arguments",
func->m_ml->ml_name);
return NULL;
}
tuple = _PyStack_AsTuple(args, nargs);
if (tuple == NULL) {
return NULL;
}
if (flags & METH_KEYWORDS) {
result = (*(PyCFunctionWithKeywords)meth) (self, tuple, kwargs);
}
else {
result = (*meth) (self, tuple);
}
Py_DECREF(tuple);
break;
}
case METH_FASTCALL:
{
PyObject **stack;
PyObject *kwnames;
_PyCFunctionFast fastmeth = (_PyCFunctionFast)meth;
if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) {
return NULL;
}
result = (*fastmeth) (self, stack, nargs, kwnames);
if (stack != args) {
PyMem_Free(stack);
}
Py_XDECREF(kwnames);
break;
}
default:
PyErr_SetString(PyExc_SystemError,
"Bad call flags in PyCFunction_Call. "
"METH_OLDARGS is no longer supported!");
return NULL;
}
result = _Py_CheckFunctionResult(func_obj, result, NULL);
return result;
}
PyObject *
PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
PyThreadState *tstate = PyThreadState_GET();
return tstate->interp->eval_frame(f, throwflag);
}
static PyObject*
_PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
PyObject *globals)
{
PyFrameObject *f;
PyThreadState *tstate = PyThreadState_GET();
PyObject **fastlocals;
Py_ssize_t i;
PyObject *result;
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;
//将参数依次赋值给 f->f_localsplus
for (i = 0; i < nargs; i++) {
Py_INCREF(*args);
fastlocals[i] = *args++;
}
result = PyEval_EvalFrameEx(f,0);
++tstate->recursion_depth;
Py_DECREF(f);
--tstate->recursion_depth;
return result;
}
#define PyFunction_GET_GLOBALS(func) \
(((PyFunctionObject *)func) -> func_globals)
#define PyFunction_GET_DEFAULTS(func) \
(((PyFunctionObject *)func) -> func_defaults)
#define PyFunction_GET_KW_DEFAULTS(func) \
(((PyFunctionObject *)func) -> func_kwdefaults)
#define PyFunction_GET_CLOSURE(func) \
(((PyFunctionObject *)func) -> func_closure)
#define PyFunction_GET_ANNOTATIONS(func) \
(((PyFunctionObject *)func) -> func_annotations)
static PyObject *
fast_function(PyObject *func, PyObject **stack,
Py_ssize_t nargs, PyObject *kwnames)
{
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
PyObject *globals = PyFunction_GET_GLOBALS(func);
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
PyObject *kwdefs, *closure, *name, *qualname;
PyObject **d;
//kwnames 为扩展的位置参数
Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
Py_ssize_t nd;
assert(PyFunction_Check(func));
assert(nargs >= 0);
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
/* kwnames must only contains str strings, no subclass, and all keys must
be unique */
PCALL(PCALL_FUNCTION);
PCALL(PCALL_FAST_FUNCTION);
if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
{ //函数不含有扩展参数的情况
if (argdefs == NULL && co->co_argcount == nargs) {
return _PyFunction_FastCall(co, stack, nargs, globals);
}
//全都是默认参数
else if (nargs == 0 && argdefs != NULL
&& co->co_argcount == Py_SIZE(argdefs)) {
/* function called with no arguments, but all parameters have
a default value: use default values as arguments .*/
stack = &PyTuple_GET_ITEM(argdefs, 0);
return _PyFunction_FastCall(co, stack, Py_SIZE(argdefs), globals);
}
}
kwdefs = PyFunction_GET_KW_DEFAULTS(func);
closure = PyFunction_GET_CLOSURE(func);
name = ((PyFunctionObject *)func) -> func_name;
qualname = ((PyFunctionObject *)func) -> func_qualname;
if (argdefs != NULL) {
d = &PyTuple_GET_ITEM(argdefs, 0);
nd = Py_SIZE(argdefs);
}
else {
d = NULL;
nd = 0;
}
return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
stack, nargs,
nkwargs ? &PyTuple_GET_ITEM(kwnames, 0) : NULL,
stack + nargs,
nkwargs, 1,
d, (int)nd, kwdefs,
closure, name, qualname);
}
#define GETLOCAL(i) (fastlocals[i])
#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \
GETLOCAL(i) = value; \
Py_XDECREF(tmp); } while (0)
static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **args, Py_ssize_t argcount,
PyObject **kwnames, PyObject **kwargs,
Py_ssize_t kwcount, int kwstep,
PyObject **defs, Py_ssize_t defcount,
PyObject *kwdefs, PyObject *closure,
PyObject *name, PyObject *qualname)
{
PyCodeObject* co = (PyCodeObject*)_co;
PyFrameObject *f;
PyObject *retval = NULL;
PyObject **fastlocals, **freevars;
PyThreadState *tstate;
PyObject *x, *u;
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
Py_ssize_t i, n;
PyObject *kwdict;
if (globals == NULL) {
PyErr_SetString(PyExc_SystemError,
"PyEval_EvalCodeEx: NULL globals");
return NULL;
}
/* Create the frame */
tstate = PyThreadState_GET();
assert(tstate != NULL);
f = PyFrame_New(tstate, co, globals, locals);
if (f == NULL) {
return NULL;
}
fastlocals = f->f_localsplus;
freevars = f->f_localsplus + co->co_nlocals;
/* Create a dictionary for keyword parameters (**kwags) */
//扩展键值对参数
if (co->co_flags & CO_VARKEYWORDS) {
kwdict = PyDict_New();
if (kwdict == NULL)
goto fail;
i = total_args;
if (co->co_flags & CO_VARARGS) {
i++;
}
SETLOCAL(i, kwdict);
}
else {
kwdict = NULL;
}
/* Copy positional arguments into local variables */
//扩展位置参数
if (argcount > co->co_argcount) {
n = co->co_argcount;
}
else {
n = argcount;
}
for (i = 0; i < n; i++) {
x = args[i];
Py_INCREF(x);
SETLOCAL(i, x);
}
/* Pack other positional arguments into the *args argument */
if (co->co_flags & CO_VARARGS) {
u = PyTuple_New(argcount - n);
if (u == NULL) {
goto fail;
}
SETLOCAL(total_args, u);
for (i = n; i < argcount; i++) {
x = args[i];
Py_INCREF(x);
PyTuple_SET_ITEM(u, i-n, x);
}
}
/* Handle keyword arguments passed as two strided arrays */
kwcount *= kwstep;
for (i = 0; i < kwcount; i += kwstep) {
PyObject **co_varnames;
PyObject *keyword = kwnames[i];
PyObject *value = kwargs[i];
Py_ssize_t j;
if (keyword == NULL || !PyUnicode_Check(keyword)) {
PyErr_Format(PyExc_TypeError,
"%U() keywords must be strings",
co->co_name);
goto fail;
}
/* Speed hack: do raw pointer compares. As names are
normally interned this should almost always hit. */
co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
for (j = 0; j < total_args; j++) {
PyObject *name = co_varnames[j];
if (name == keyword) {
goto kw_found;
}
}
/* Slow fallback, just in case */
for (j = 0; j < total_args; j++) {
PyObject *name = co_varnames[j];
int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ);
if (cmp > 0) {
goto kw_found;
}
else if (cmp < 0) {
goto fail;
}
}
if (j >= total_args && kwdict == NULL) {
PyErr_Format(PyExc_TypeError,
"%U() got an unexpected keyword argument '%S'",
co->co_name, keyword);
goto fail;
}
if (PyDict_SetItem(kwdict, keyword, value) == -1) {
goto fail;
}
continue;
kw_found:
if (GETLOCAL(j) != NULL) {
PyErr_Format(PyExc_TypeError,
"%U() got multiple values for argument '%S'",
co->co_name, keyword);
goto fail;
}
Py_INCREF(value);
SETLOCAL(j, value);
}
/* Check the number of positional arguments */
if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) {
too_many_positional(co, argcount, defcount, fastlocals);
goto fail;
}
/* Add missing positional arguments (copy default values from defs) */
if (argcount < co->co_argcount) {
Py_ssize_t m = co->co_argcount - defcount;
Py_ssize_t missing = 0;
for (i = argcount; i < m; i++) {
if (GETLOCAL(i) == NULL) {
missing++;
}
}
if (missing) {
missing_arguments(co, missing, defcount, fastlocals);
goto fail;
}
if (n > m)
i = n - m;
else
i = 0;
for (; i < defcount; i++) {
if (GETLOCAL(m+i) == NULL) {
PyObject *def = defs[i];
Py_INCREF(def);
SETLOCAL(m+i, def);
}
}
}
/* Add missing keyword arguments (copy default values from kwdefs) */
if (co->co_kwonlyargcount > 0) {
Py_ssize_t missing = 0;
for (i = co->co_argcount; i < total_args; i++) {
PyObject *name;
if (GETLOCAL(i) != NULL)
continue;
name = PyTuple_GET_ITEM(co->co_varnames, i);
if (kwdefs != NULL) {
PyObject *def = PyDict_GetItem(kwdefs, name);
if (def) {
Py_INCREF(def);
SETLOCAL(i, def);
continue;
}
}
missing++;
}
if (missing) {
missing_arguments(co, missing, -1, fastlocals);
goto fail;
}
}
/* Allocate and initialize storage for cell vars, and copy free
vars into frame. */
for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
PyObject *c;
int arg;
/* Possibly account for the cell variable being an argument. */
if (co->co_cell2arg != NULL &&
(arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) {
c = PyCell_New(GETLOCAL(arg));
/* Clear the local copy. */
SETLOCAL(arg, NULL);
}
else {
c = PyCell_New(NULL);
}
if (c == NULL)
goto fail;
//cellvars 在 locals 变量之后
SETLOCAL(co->co_nlocals + i, c);
}
/* Copy closure variables to free variables */
for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
PyObject *o = PyTuple_GET_ITEM(closure, i);
Py_INCREF(o);
freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
}
/* Handle generator/coroutine/asynchronous generator */
if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
PyObject *gen;
PyObject *coro_wrapper = tstate->coroutine_wrapper;
int is_coro = co->co_flags & CO_COROUTINE;
if (is_coro && tstate->in_coroutine_wrapper) {
assert(coro_wrapper != NULL);
PyErr_Format(PyExc_RuntimeError,
"coroutine wrapper %.200R attempted "
"to recursively wrap %.200R",
coro_wrapper,
co);
goto fail;
}
/* Don't need to keep the reference to f_back, it will be set
* when the generator is resumed. */
Py_CLEAR(f->f_back);
PCALL(PCALL_GENERATOR);
/* Create a new generator that owns the ready to run frame
* and return that as the value. */
if (is_coro) {
gen = PyCoro_New(f, name, qualname);
} else if (co->co_flags & CO_ASYNC_GENERATOR) {
gen = PyAsyncGen_New(f, name, qualname);
} else {
gen = PyGen_NewWithQualName(f, name, qualname);
}
if (gen == NULL)
return NULL;
if (is_coro && coro_wrapper != NULL) {
PyObject *wrapped;
tstate->in_coroutine_wrapper = 1;
wrapped = PyObject_CallFunction(coro_wrapper, "N", gen);
tstate->in_coroutine_wrapper = 0;
return wrapped;
}
return gen;
}
retval = PyEval_EvalFrameEx(f,0);
fail: /* Jump here from prelude on failure */
/* decref'ing the frame can cause __del__ methods to get invoked,
which can call back into Python. While we're done with the
current Python frame (f), the associated C stack is still in use,
so recursion_depth must be boosted for the duration.
*/
assert(tstate != NULL);
++tstate->recursion_depth;
Py_DECREF(f);
--tstate->recursion_depth;
return retval;
}
PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
PyObject *locals)
{
PyFrameObject *back = tstate->frame;
PyFrameObject *f;
PyObject *builtins;
Py_ssize_t i;
if (back == NULL || back->f_globals != globals) {
builtins = _PyDict_GetItemId(globals, &PyId___builtins__);
if (builtins) {
if (PyModule_Check(builtins)) {
builtins = PyModule_GetDict(builtins);
assert(builtins != NULL);
}
}
if (builtins == NULL) {
/* No builtins! Make up a minimal one
Give them 'None', at least. */
builtins = PyDict_New();
if (builtins == NULL ||
PyDict_SetItemString(
builtins, "None", Py_None) < 0)
return NULL;
}
else
Py_INCREF(builtins);
}
else {
/* If we share the globals, we share the builtins.
Save a lookup and a call. */
builtins = back->f_builtins;
assert(builtins != NULL);
Py_INCREF(builtins);
}
if (code->co_zombieframe != NULL) {
f = code->co_zombieframe;
code->co_zombieframe = NULL;
_Py_NewReference((PyObject *)f);
assert(f->f_code == code);
}
else {
Py_ssize_t extras, ncells, nfrees;
ncells = PyTuple_GET_SIZE(code->co_cellvars);
nfrees = PyTuple_GET_SIZE(code->co_freevars);
//代码段的各个变量要在新建的 PyFrameObject 中保留
extras = code->co_stacksize + code->co_nlocals + ncells +
nfrees;
if (free_list == NULL) {
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type,
extras);
if (f == NULL) {
Py_DECREF(builtins);
return NULL;
}
}
else {
assert(numfree > 0);
--numfree;
f = free_list;
free_list = free_list->f_back;
if (Py_SIZE(f) < extras) {
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
if (new_f == NULL) {
PyObject_GC_Del(f);
Py_DECREF(builtins);
return NULL;
}
f = new_f;
}
_Py_NewReference((PyObject *)f);
}
f->f_code = code;
//将调用者的 PyCodeObject 的 co_nlocals n_cells, nfrees 赋值给 f_localsplus
extras = code->co_nlocals + ncells + nfrees; //没有包含 code->stacksize
f->f_valuestack = f->f_localsplus + extras;
for (i=0; i<extras; i++)
f->f_localsplus[i] = NULL;
f->f_locals = NULL;
f->f_trace = NULL;
f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL;
}
f->f_stacktop = f->f_valuestack;
f->f_builtins = builtins;
Py_XINCREF(back);
f->f_back = back;
Py_INCREF(code);
Py_INCREF(globals);
f->f_globals = globals;
/* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */
if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) ==
(CO_NEWLOCALS | CO_OPTIMIZED))
; /* f_locals = NULL; will be set by PyFrame_FastToLocals() */
else if (code->co_flags & CO_NEWLOCALS) {
locals = PyDict_New();
if (locals == NULL) {
Py_DECREF(f);
return NULL;
}
f->f_locals = locals;
}
else {
//用全局变量初始化局部变量
if (locals == NULL)
locals = globals;
Py_INCREF(locals);
f->f_locals = locals;
}
f->f_lasti = -1;
f->f_lineno = code->co_firstlineno;
f->f_iblock = 0;
f->f_executing = 0;
f->f_gen = NULL;
_PyObject_GC_TRACK(f);
return f;
}
对于 C 函数,调用 (func->m_ml -> ml_meth)(args) 参数,这里参数包括没有参数,
一个参数,可变参数
对于 Python 本身的函数调用 PyEval_EvalFrameEx,对应无参数的进行了优化,有参数的
进行各种解析。
至此,我们应该对 Python 的执行机制有了更加深刻的理解。大体描述如下:
编译的时候,每个函数会编译为一个单独的 PyCodeObject;
运行的是时候首先,整个文件是一个 PyFrameObject 对象,每个 PyFrameObject 对象包含
PyCodeObject,在 PyCodeObject 执行过程中遇到函数调用,会创建一个新的 PyFunctionObject 对象,
并将编译的 PyCodeObject 赋值给该对象,根据函数类型判断是 C 函数还是 Python
本身的函数。如果是 C 函数, 对调用 (func->m_ml -> ml_meth)(args),如果是 Python
函数,会创建一个新的 PyFrameObject,并用 PyFunctionObject 初始化该 PyFrameObject
对象,最后执行 PyFunctionObject 中 PyCodeObject 中的字节码。
此外,还有一个细节需要注意的是 f_globals,虽然所有函数共享全局变量,但是,
事实上每个函数访问全局变量的时候,并不是依次向 上搜索,而是在调用的是,
直接将父函数的 globals 传递给子函数。这样子函数直接搜索自身的 f_globals
就可以了。这样虽然占用了内存,但是提高了效率。
两个问题:
1. 递归是怎么实现的?
2. 前面定义的函数可以调用后面定义的函数么? 为什么?
python 中参数包括
例如:
#!/usr/bin/env python
# encoding: utf-8
def test():
pass
def test1(a):
pass
def test2(a, b):
pass
def test3(a, b, *argv):
pass
def test4(a, b, **argv):
pass
test()
test1(1)
test2(1, 2)
test3(1, 2, 3)
test3(1, 2, 3, 4)
test4(1, 2, c = 3, d = 4)
编译之后
magic b'330d0d0a'
moddate b'8788345b' (Thu Jun 28 15:04:39 2018)
files sz 803
code
argcount 0
nlocals 0
stacksize 6
flags 0040
code
b'6400640184005a006402640384005a016404640584005a02640664078400'
b'5a036408640984005a046500830001006501640a830101006502640a640b'
b'830201006503640a640b640c830301006503640a640b640c640d83040100'
b'6504640a640b640c640d640e8d040100640f5300'
4 0 LOAD_CONST 0 (<code object test at 0x103828ae0, file "/tmp/a.py", line 4>)
2 LOAD_CONST 1 ('test')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (test)
7 8 LOAD_CONST 2 (<code object test1 at 0x103839ae0, file "/tmp/a.py", line 7>)
10 LOAD_CONST 3 ('test1')
12 MAKE_FUNCTION 0
14 STORE_NAME 1 (test1)
10 16 LOAD_CONST 4 (<code object test2 at 0x103839f60, file "/tmp/a.py", line 10>)
18 LOAD_CONST 5 ('test2')
20 MAKE_FUNCTION 0
22 STORE_NAME 2 (test2)
13 24 LOAD_CONST 6 (<code object test3 at 0x103837b70, file "/tmp/a.py", line 13>)
26 LOAD_CONST 7 ('test3')
28 MAKE_FUNCTION 0
30 STORE_NAME 3 (test3)
16 32 LOAD_CONST 8 (<code object test4 at 0x1038375d0, file "/tmp/a.py", line 16>)
34 LOAD_CONST 9 ('test4')
36 MAKE_FUNCTION 0
38 STORE_NAME 4 (test4)
19 40 LOAD_NAME 0 (test)
42 CALL_FUNCTION 0 //没参数,直接调用
44 POP_TOP
20 46 LOAD_NAME 1 (test1)
48 LOAD_CONST 10 (1)
50 CALL_FUNCTION 1 //一个参数
52 POP_TOP
21 54 LOAD_NAME 2 (test2)
56 LOAD_CONST 10 (1)
58 LOAD_CONST 11 (2)
60 CALL_FUNCTION 2 //两个参数
62 POP_TOP
22 64 LOAD_NAME 3 (test3)
66 LOAD_CONST 10 (1)
68 LOAD_CONST 11 (2)
70 LOAD_CONST 12 (3)
72 CALL_FUNCTION 3 //三个参数
74 POP_TOP
23 76 LOAD_NAME 3 (test3)
78 LOAD_CONST 10 (1)
80 LOAD_CONST 11 (2)
82 LOAD_CONST 12 (3)
84 LOAD_CONST 13 (4) //四个参数
86 CALL_FUNCTION 4
88 POP_TOP
24 90 LOAD_NAME 4 (test4)
92 LOAD_CONST 10 (1)
94 LOAD_CONST 11 (2)
96 LOAD_CONST 12 (3)
98 LOAD_CONST 13 (4)
100 LOAD_CONST 14 (('c', 'd')) //键值对
102 CALL_FUNCTION_KW 4 //四个参数
104 POP_TOP
106 LOAD_CONST 15 (None)
108 RETURN_VALUE
consts
code
argcount 0
nlocals 0 //没有参数,没有局部变量
stacksize 1
flags 0043
code b'64005300'
5 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
consts
None
names ()
varnames ()
freevars ()
cellvars ()
filename '/tmp/a.py'
name 'test'
firstlineno 4
lnotab b'0001'
'test'
code
argcount 1
nlocals 1 //一个参数,一个局部变量
stacksize 1
flags 0043
code b'64005300'
8 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
consts
None
names ()
varnames ('a',)
freevars ()
cellvars ()
filename '/tmp/a.py'
name 'test1'
firstlineno 7
lnotab b'0001'
'test1'
code
argcount 2
nlocals 2 //两个参数,两个局部变量
stacksize 1
flags 0043
code b'64005300'
11 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
consts
None
names ()
varnames ('a', 'b')
freevars ()
cellvars ()
filename '/tmp/a.py'
name 'test2'
firstlineno 10
lnotab b'0001'
'test2'
code
argcount 2
nlocals 3 //两个参数,三个局部变量,其中一个为 argv
stacksize 1
flags 0047
code b'64005300'
14 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
consts
None
names ()
varnames ('a', 'b', 'argv')
freevars ()
cellvars ()
filename '/tmp/a.py'
name 'test3'
firstlineno 13
lnotab b'0001'
'test3'
code
argcount 2
nlocals 3 //两个参数,三个局部变量,其中一个为 argv
stacksize 1
flags 004b
code b'64005300'
17 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
consts
None
names ()
varnames ('a', 'b', 'argv')
freevars ()
cellvars ()
filename '/tmp/a.py'
name 'test4'
firstlineno 16
lnotab b'0001'
'test4'
1
2
3
4
('c', 'd')
None
names ('test', 'test1', 'test2', 'test3', 'test4')
varnames ()
freevars ()
cellvars ()
filename '/tmp/a.py'
name '<module>'
firstlineno 4
lnotab b'08030803080308030803060108010a010c010e01'
当包含位置参数的时候,位置参数会保存在局部变量中,之后传递给函数。你可以试着编译如下代码来验证自己的想法。
def test5(a, b, *args, **argv):
pass
test5(1, 2, 3, 4, c = 5, d = 6)
函数参数从左到右依次压入栈中,才实际调用的时候,会创建
PyFrameObject,这些参数会传递给 PyFrameObject 的 f_localsplus,
当访问函数参数的时候,直接通过索引定位到 f_localsplus 中的参数,
这也是位置参数的由来。
从 MAKE_FUNCTION 中可以看出,python 中包括 5 中函数
而且以上五中并不是互斥的,他们可任意组合。
TARGET(CALL_FUNCTION_KW) {
PyObject **sp, *res, *names;
names = POP();
assert(PyTuple_CheckExact(names) && PyTuple_GET_SIZE(names) <= oparg);
PCALL(PCALL_ALL);
sp = stack_pointer;
res = call_function(&sp, oparg, names);
stack_pointer = sp;
PUSH(res);
Py_DECREF(names);
if (res == NULL) {
goto error;
}
DISPATCH();
}
TARGET(CALL_FUNCTION_EX) {
PyObject *func, *callargs, *kwargs = NULL, *result;
PCALL(PCALL_ALL);
if (oparg & 0x01) {
kwargs = POP();
if (!PyDict_CheckExact(kwargs)) {
PyObject *d = PyDict_New();
if (d == NULL)
goto error;
if (PyDict_Update(d, kwargs) != 0) {
Py_DECREF(d);
/* PyDict_Update raises attribute
* error (percolated from an attempt
* to get 'keys' attribute) instead of
* a type error if its second argument
* is not a mapping.
*/
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
format_kwargs_mapping_error(SECOND(), kwargs);
}
Py_DECREF(kwargs);
goto error;
}
Py_DECREF(kwargs);
kwargs = d;
}
assert(PyDict_CheckExact(kwargs));
}
callargs = POP();
func = TOP();
if (!PyTuple_CheckExact(callargs)) {
if (check_args_iterable(func, callargs) < 0) {
Py_DECREF(callargs);
goto error;
}
Py_SETREF(callargs, PySequence_Tuple(callargs));
if (callargs == NULL) {
goto error;
}
}
assert(PyTuple_CheckExact(callargs));
result = do_call_core(func, callargs, kwargs);
Py_DECREF(func);
Py_DECREF(callargs);
Py_XDECREF(kwargs);
SET_TOP(result);
if (result == NULL) {
goto error;
}
DISPATCH();
}
static PyObject *
do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict)
{
if (PyCFunction_Check(func)) {
PyObject *result;
PyThreadState *tstate = PyThreadState_GET();
C_TRACE(result, PyCFunction_Call(func, callargs, kwdict));
return result;
}
else {
return PyObject_Call(func, callargs, kwdict);
}
}
PyObject *
PyObject_Call(PyObject *func, PyObject *args, PyObject *kwargs)
{
ternaryfunc call;
PyObject *result;
/* PyObject_Call() must not be called with an exception set,
because it may clear it (directly or indirectly) and so the
caller loses its exception */
assert(!PyErr_Occurred());
assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs));
call = func->ob_type->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
func->ob_type->tp_name);
return NULL;
}
if (Py_EnterRecursiveCall(" while calling a Python object"))
return NULL;
result = (*call)(func, args, kwargs);
Py_LeaveRecursiveCall();
return _Py_CheckFunctionResult(func, result, NULL);
}
#!/usr/bin/env python
# encoding: utf-8
def test1(a):
a = 1
b = 2
c = 3
test1(3)
#!/usr/bin/env python
# encoding: utf-8
def cmp_closure(base):
v = 19
def cmp_to(value):
if value > 20:
value = v
return base < value
return cmp_to
def cmp_noclosure(base):
v = 19
def cmp_to(value, base = base):
if value > 20:
value = v
return base < value
return cmp_to
cmp1 = cmp_closure(10)
print(cmp1(9))
print(cmp1(11))
cmp1 = cmp_noclosure(10)
print(cmp1(9))
print(cmp1(11))
print(cmp1(12, 13))
magic b'330d0d0a'
moddate b'e4a6345b' (Thu Jun 28 17:14:12 2018)
files sz 1678
code
argcount 0
nlocals 0
stacksize 4
flags 0040
code
b'6400640184005a006402640384005a016500640483015a02650365026405'
b'8301830101006503650264068301830101006501640483015a0265036502'
b'640583018301010065036502640683018301010065036502640764088302'
b'8301010064095300'
4 0 LOAD_CONST 0 (<code object cmp_closure at 0x103072c00, file "/tmp/a.py", line 4>)
2 LOAD_CONST 1 ('cmp_closure')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (cmp_closure)
13 8 LOAD_CONST 2 (<code object cmp_noclosure at 0x103083ae0, file "/tmp/a.py", line 13>)
10 LOAD_CONST 3 ('cmp_noclosure')
12 MAKE_FUNCTION 0
14 STORE_NAME 1 (cmp_noclosure)
22 16 LOAD_NAME 0 (cmp_closure)
18 LOAD_CONST 4 (10)
20 CALL_FUNCTION 1
22 STORE_NAME 2 (cmp1)
23 24 LOAD_NAME 3 (print)
26 LOAD_NAME 2 (cmp1)
28 LOAD_CONST 5 (9)
30 CALL_FUNCTION 1
32 CALL_FUNCTION 1
34 POP_TOP
24 36 LOAD_NAME 3 (print)
38 LOAD_NAME 2 (cmp1)
40 LOAD_CONST 6 (11)
42 CALL_FUNCTION 1
44 CALL_FUNCTION 1
46 POP_TOP
26 48 LOAD_NAME 1 (cmp_noclosure)
50 LOAD_CONST 4 (10)
52 CALL_FUNCTION 1
54 STORE_NAME 2 (cmp1)
27 56 LOAD_NAME 3 (print)
58 LOAD_NAME 2 (cmp1)
60 LOAD_CONST 5 (9)
62 CALL_FUNCTION 1
64 CALL_FUNCTION 1
66 POP_TOP
28 68 LOAD_NAME 3 (print)
70 LOAD_NAME 2 (cmp1)
72 LOAD_CONST 6 (11)
74 CALL_FUNCTION 1
76 CALL_FUNCTION 1
78 POP_TOP
29 80 LOAD_NAME 3 (print)
82 LOAD_NAME 2 (cmp1)
84 LOAD_CONST 7 (12)
86 LOAD_CONST 8 (13)
88 CALL_FUNCTION 2
90 CALL_FUNCTION 1
92 POP_TOP
94 LOAD_CONST 9 (None)
96 RETURN_VALUE
consts
code
argcount 1
nlocals 2
stacksize 3
flags 0003
code b'640189018700870166026402640384087d017c015300'
5 0 LOAD_CONST 1 (19)
2 STORE_DEREF 1 (v)
6 4 LOAD_CLOSURE 0 (base)
6 LOAD_CLOSURE 1 (v)
8 BUILD_TUPLE 2
10 LOAD_CONST 2 (<code object cmp_to at 0x103072ae0, file "/tmp/a.py", line 6>)
12 LOAD_CONST 3 ('cmp_closure.<locals>.cmp_to')
14 MAKE_FUNCTION 8
16 STORE_FAST 1 (cmp_to)
11 18 LOAD_FAST 1 (cmp_to)
20 RETURN_VALUE
consts
None
19
code
argcount 1
nlocals 1
stacksize 2
flags 0013
code b'7c0064016b04720c88017d0088007c006b005300'
7 0 LOAD_FAST 0 (value)
2 LOAD_CONST 1 (20)
4 COMPARE_OP 4 (>)
6 POP_JUMP_IF_FALSE 12
8 8 LOAD_DEREF 1 (v)
10 STORE_FAST 0 (value)
9 >> 12 LOAD_DEREF 0 (base)
14 LOAD_FAST 0 (value)
16 COMPARE_OP 0 (<)
18 RETURN_VALUE
consts
None
20
names ()
varnames ('value',)
freevars ('base', 'v')
cellvars ()
filename '/tmp/a.py'
name 'cmp_to'
firstlineno 6
lnotab b'000108010401'
'cmp_closure.<locals>.cmp_to'
names ()
varnames ('base', 'cmp_to')
freevars ()
cellvars ('base', 'v')
filename '/tmp/a.py'
name 'cmp_closure'
firstlineno 4
lnotab b'000104010e05'
'cmp_closure'
code
argcount 1
nlocals 2
stacksize 4
flags 0003
code b'640189007c006601870066016402640384097d017c015300'
14 0 LOAD_CONST 1 (19)
2 STORE_DEREF 0 (v)
15 4 LOAD_FAST 0 (base)
6 BUILD_TUPLE 1
8 LOAD_CLOSURE 0 (v)
10 BUILD_TUPLE 1
12 LOAD_CONST 2 (<code object cmp_to at 0x103083f60, file "/tmp/a.py", line 15>)
14 LOAD_CONST 3 ('cmp_noclosure.<locals>.cmp_to')
16 MAKE_FUNCTION 9 //这里会初始化 func_closure, func_defaults
18 STORE_FAST 1 (cmp_to)
20 20 LOAD_FAST 1 (cmp_to)
22 RETURN_VALUE
consts
None
19
code
argcount 2
nlocals 2
stacksize 2
flags 0013
code b'7c0064016b04720c88007d007c017c006b005300'
16 0 LOAD_FAST 0 (value)
2 LOAD_CONST 1 (20)
4 COMPARE_OP 4 (>)
6 POP_JUMP_IF_FALSE 12
17 8 LOAD_DEREF 0 (v)
10 STORE_FAST 0 (value)
18 >> 12 LOAD_FAST 1 (base)
14 LOAD_FAST 0 (value)
16 COMPARE_OP 0 (<)
18 RETURN_VALUE
consts
None
20
names ()
varnames ('value', 'base')
freevars ('v',)
cellvars ()
filename '/tmp/a.py'
name 'cmp_to'
firstlineno 15
lnotab b'000108010401'
'cmp_noclosure.<locals>.cmp_to'
names ()
varnames ('base', 'cmp_to')
freevars ()
cellvars ('v',)
filename '/tmp/a.py'
name 'cmp_noclosure'
firstlineno 13
lnotab b'000104011005'
'cmp_noclosure'
10
9
11
12
13
None
names ('cmp_closure', 'cmp_noclosure', 'cmp1', 'print')
varnames ()
freevars ()
cellvars ()
filename '/tmp/a.py'
name '<module>'
firstlineno 4
lnotab b'0809080908010c010c0208010c010c01'
字节码可以看出,
1. 调用 cmp_closure ,返回一个函数对象
2. 调用 cmp1 指向的函数
符合 Lazy 加载模式
TARGET(LOAD_DEREF) {
PyObject *cell = freevars[oparg];
PyObject *value = PyCell_GET(cell);
if (value == NULL) {
format_exc_unbound(co, oparg);
goto error;
}
Py_INCREF(value);
PUSH(value);
DISPATCH();
}
TARGET(STORE_DEREF) {
PyObject *v = POP();
PyObject *cell = freevars[oparg];
PyObject *oldobj = PyCell_GET(cell);
PyCell_SET(cell, v);
Py_XDECREF(oldobj);
DISPATCH();
}
PyObject *
PyCell_New(PyObject *obj)
{
PyCellObject *op;
op = (PyCellObject *)PyObject_GC_New(PyCellObject, &PyCell_Type);
if (op == NULL)
return NULL;
op->ob_ref = obj;
Py_XINCREF(obj);
_PyObject_GC_TRACK(op);
return (PyObject *)op;
}
PyObject *
PyCell_Get(PyObject *op)
{
if (!PyCell_Check(op)) {
PyErr_BadInternalCall();
return NULL;
}
Py_XINCREF(((PyCellObject*)op)->ob_ref);
return PyCell_GET(op);
}
int
PyCell_Set(PyObject *op, PyObject *obj)
{
PyObject* oldobj;
if (!PyCell_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
oldobj = PyCell_GET(op);
Py_XINCREF(obj);
PyCell_SET(op, obj);
Py_XDECREF(oldobj);
return 0;
}
TARGET(LOAD_FAST) {
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
format_exc_check_arg(PyExc_UnboundLocalError,
UNBOUNDLOCAL_ERROR_MSG,
PyTuple_GetItem(co->co_varnames, oparg));
goto error;
}
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
TARGET(BUILD_TUPLE) {
PyObject *tup = PyTuple_New(oparg);
if (tup == NULL)
goto error;
while (--oparg >= 0) {
PyObject *item = POP();
PyTuple_SET_ITEM(tup, oparg, item);
}
PUSH(tup);
DISPATCH();
}
TARGET(LOAD_CLOSURE) {
PyObject *cell = freevars[oparg];
Py_INCREF(cell);
PUSH(cell);
DISPATCH();
}
TARGET(STORE_FAST) {
PyObject *value = POP();
SETLOCAL(oparg, value);
FAST_DISPATCH();
}
TODO
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。