CPython中的函数对象和代码对象关系

时间:2019-07-13 20:05:08

标签: python cpython

CPython 源代码的Include/funcobject.h以下一条注释开头:

  

函数对象和代码对象不应与每个对象混淆   其他:

     

功能对象是通过执行'def'创建的   声明。他们在其__code__属性中引用了一个代码对象,   这纯粹是句法对象,即只不过是编译对象   一些源代码行的版本。每个源只有一个代码对象   代码“片段”,,但每个代码对象都可以用零或   许多功能对象仅取决于“ def”的次数   到目前为止,源代码中的语句已经执行。

我不太了解。


我在这里写下我的部分理解。可能有人完成它。

  1. 编译阶段。

    我们有源文件 Test.py

    def a_func():
        pass
    

    解释器解析它并创建两个代码对象-一个用于Test.py,一个用于a_funcTest.py代码对象具有co_code字段(已反汇编):

      3           0 LOAD_CONST               0 (<code object a_func at 0x7f8975622b70, file "test.py", line 3>)
                  2 LOAD_CONST               1 ('a_func')
                  4 MAKE_FUNCTION            0
                  6 STORE_NAME               0 (a_func)
                  8 LOAD_CONST               2 (None)
                 10 RETURN_VALUE
    

    现阶段未创建任何函数对象。

  2. 执行阶段。

    •   

      函数对象是通过执行'def'语句创建的。

    虚拟机到达MAKE_FUNCTION指令时,它将创建函数对象:

    typedef struct {
        PyObject_HEAD
        PyObject *func_code;        /* A code object, 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, 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 */
        PyObject *func_annotations; /* Annotations, a dict or NULL */
        PyObject *func_qualname;    /* The qualified name */
    } PyFunctionObject;
    
    •   

      他们在自己的__code__属性中引用了一个代码对象,这是一个纯粹的语法对象,即仅是某些源代码行的编译版本。

    ,并将a_func代码对象放入PyObject *func_code字段中。现在,注释“功能对象和代码对象不相同”中的消息已清除。

    •   

      每个源代码“片段”都有一个代码对象,,但是每个代码对象可以被零个或多个功能对象引用,这取决于到目前为止,源代码中的“ def”语句执行了多少次。

    坚固的字体强调了我不理解的部分。

1 个答案:

答案 0 :(得分:1)

如果我创建了一个lambda工厂(出于范围考虑,这是一个好主意):

def mk_const(k):
  def const(): return k
  return const

然后有一个用于mk_const的代码对象和一个用于const的代码对象,但是后者有许多函数对象作为对mk_const的调用(包括0)。

(使用lambda没什么区别,但是使用def解释起来更容易。)

也可能是if的结果:

if lib.version>=4:
  def f(x): return lib.pretty(x)
else:
  def f(x): return str(x)  # fallback

这里有两个代码对象(加上一个用于模块的对象),但最多只能使用其中一个。