Python 原始碼分析之函式機制
在 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 中引數包括
- 位置引數,如 f(a, b)
- 鍵值對引數,如 f(a = 1, b = 2)
- 擴充套件位置引數,如 f(a, b, *argv)
- 擴充套件鍵值對引數,如 f(a, b, **argv)
例如:
#!/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
相關推薦
Python 原始碼分析之函式機制
在 python 中函式也是一個物件
typedef struct {
PyObject_HEAD
PyObject *func_code; /* 函式編譯之後的 PyCodeObject, the __code__ attribute */
PyOb
Python 原始碼分析之位元組碼之基本操作
本文基於 Python 3.6.4 編譯器生成位元組碼,你可通過如下程式碼片段得到 python 原始碼對應的位元組碼
#!/usr/bin/env python
# encoding: utf-8
import sys
import dis
filename=sys.argv
Python 原始碼分析之執行時環境
python 執行時環境
執行環境是一個全域性的概念,而執行環境就是指棧幀
當執行時環境已經準備好的時候,執行第一行程式碼的函式就是 PyEval_EvalFrame 函式
PyObject *
PyEval_EvalFrame(PyFrameObject *f) {
python 原始碼分析之型別系統
型別系統
一般物件是不能靜態分配的,而 python 所有內建物件都是靜態分配的
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject
Python 原始碼分析之初體驗
在 python 中,物件就是 C 中結構體在堆上申請的一塊記憶體,一般來說, 物件不能靜態初始化,並且也不能在棧上空間生存。唯一的例外就是型別 物件,python 中所有的內建的型別物件(整數型別物件,字元型別物件) 都是靜態初始化的
python 架構
外部呼叫
Android原始碼分析之訊息機制——Handler原始碼解析
Android的訊息機制主要是指Handler的執行機制,Handler是Android訊息機制上層介面的實現,它的執行需要Message、MessageQueue和Looper的支撐,下面就來分別介紹它們的實現原理。
1、Message原始碼解析
首先來了解一下Messag
Memcached原始碼分析之儲存機制Slabs(7)
文章列表:
《Memcached原始碼分析 - Memcached原始碼分析之總結篇(8)》前言
這一章我們重點講解Memcached的儲存機制Slabs。Memcached儲存Item的程式碼都是在slabs.c中來實現的。
在解讀這一章前,我們必須先了
JVM原始碼分析之Attach機制實現完全解讀
Attach是什麼
在講這個之前,我們先來點大家都知道的東西,當我們感覺執行緒一直卡在某個地方,想知道卡在哪裡,首先想到的是進行執行緒dump,而常用的命令是jstack ,我們就可以看到如下執行緒棧了
2014-06-18 12:56:14
Full thread dump Java HotSpot(
Android事件分發機制原始碼分析之Activity篇
在之前的事件分發分析中,曾提及到View的事件是由ViewGroup分發的,然而ViewGroup的事件我們只是稍微帶過是由Activity分發的。而我們知道,事件產生於使用者按下螢幕的一瞬間,事件生成後,經過一系列的過程來到我們的Activity層,那麼事件是怎樣從Activity傳遞
ipset原始碼分析之kadt和uadt回撥函式
一定要清楚自己在幹什麼,每行程式碼在幹什麼,這樣寫的程式碼才能做到心中有數。
之前看到ip_set_hash_ip.chash_ip4_uadt和hash_ip4_kadt函式,就一直很好奇這兩個函式是幹什麼呢?
下面我來帶你一步步剖析這兩個函式
一、kadt和uadt回撥函式的註冊
Hadoop原始碼分析之二(RPC機制之Call處理)
下面介紹在整個處理機制中怎麼把具體的Request Call轉換並呼叫到整體的實現邏輯。
主要以NameNode Client PRC Server作為例子來說明,整個轉換通過Google Protocol Buffer RPC來實現。
fina
STL原始碼分析之vector(二)—核心函式 push_back及insert_aux
說明: STL原始碼分析系列部落格的使用的是https://www.sgi.com/tech/stl/download.html 裡面的STL v2.03版.不同的STL或許會有所不同。
其它vector內容請參照本系列其它部落格。
主要函式分析
Android Wi-Fi原始碼分析之WifiService操作Wi-Fi(一):分析Wifi.c中的wifi_load_driver()函式
Wi-Fi原始碼分析之WifiService操作Wi-Fi(一)
分析Wifi.c中的wifi_load_driver()函式
int wifi_load_driver()
{
AL
(三)ghostscript原始碼分析之interp函式
interp是核心函式,理解此函式要下極大的功夫。
對有些及難懂的註釋了一些。
交流分享是一種學習的好方法。
/* Main interpreter. */
/* If execution terminates normally, return e_InterpreterExit. */
(二)ghostscript原始碼分析之interp()函式之IREF_NEXT巨集分析
interp()函式用了大量的巨集。IREF_NEXT只是其中一個巨集,但是出現的頻率很高。
但是透徹的理解這個巨集將為理解interp()函式提供便利。
它的定義形式如下:
#define IREF_NEXT(ip)/
((const ref_packed *)((const re
(一)ghostscript原始碼分析之interp()函式的第二個引數
/* Main interpreter. */ /* If execution terminates normally, return e_InterpreterExit. */ /* If an error occurs, leave the current object in *perror_o
ghostscript原始碼分析之 scan_token()函式 (詞法分析器iscan.c)
scan_token()函式很重要,ghostscript寫得比較瑣碎難懂,裡面有些有英文解釋。
我只對我關注的部分加了些中文註釋。當然不是所有的都理解了。但是功能還是清楚了的,像某些函式介面。
如果讓我寫的話,我一定比他寫得更清晰。哈哈,當然他的scan_token基本的框架
設計還是
spark原始碼分析之Master原始碼主備切換機制分析
Master原始碼分析之主備切換機制
1.當選為leader之後的操作
//ElectedLeader 當選leader
case ElectedLeader => {
Android Wi-Fi原始碼分析之wpa_supplicant初始化(四):wpa_supplicant_init_iface函式分析
wpa_config_read分析
路徑為:external\wpa_supplicant_8\wpa_supplicant\config_file.c
struct wpa_config * wpa_config_read(const char *na
python效能優化之函式執行時間分析
最近發現專案API請求比較慢,通過抓包發現主要是response時間太長,於是就開始進行優化工作。優化工作的關鍵一步是定位出問題的瓶頸,對於優化速度來說,從優化函式執行時間這個維度去切入是一個不錯的選擇。
本文側重分析,不展開如何優化
利器
工欲善其事,必先利其器,我們需要一套方便高效的工具記