typedef struct { PyObject_HEAD int co_argcount; /* #arguments, except *args */ int co_kwonlyargcount; /* #keyword only arguments */ int co_nlocals; /* #local variables */ int co_stacksize; /* #entries needed for evaluation stack */ int co_flags; /* CO_..., see below */ PyObject *co_code; /* instruction opcodes */ PyObject *co_consts; /* list (constants used) */ PyObject *co_names; /* list of strings (names used) */ PyObject *co_varnames; /* tuple of strings (local variable names) */ PyObject *co_freevars; /* tuple of strings (free variable names) */ PyObject *co_cellvars; /* tuple of strings (cell variable names) */ /* The rest aren't used in either hash or comparisons, except for co_name (used in both) and co_firstlineno (used only in comparisons). This is done to preserve the name and line number for tracebacks and debuggers; otherwise, constant de-duplication would collapse identical functions/lambdas defined on different lines. */ unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */ PyObject *co_filename; /* unicode (where it was loaded from) */ PyObject *co_name; /* unicode (name, for reference) */ int co_firstlineno; /* first source line number */ PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See Objects/lnotab_notes.txt for details. */ void *co_zombieframe; /* for optimization only (see frameobject.c) */ PyObject *co_weakreflist; /* to support weakrefs to code objects */ } PyCodeObject;
コード オブジェクトの各フィールドの役割は次のとおりです。
まず、次の概念を理解する必要があります。コード ブロック: いわゆるコード ブロックは、小さな Python コードが小さな単位として実行されます。 Python の一般的なコード ブロックには、関数本体、クラス定義、モジュールが含まれます。
argcount、これはコード ブロック内のパラメーターの数を表します。関数には上記の pycdemo.py などのパラメーターがある可能性があるため、このパラメーターは関数本体のコード ブロックでのみ役立ちます。は関数ではなくモジュールです。このパラメータの対応する値は 0 です。
co_code、このオブジェクトの特定のコンテンツは、実際の Python バイトコードを格納するバイト シーケンスです。これは主に Python 仮想マシンの実行に使用され、この記事には含まれません。詳細な分析。
co_consts、このフィールドはリスト タイプのフィールドで、主に "__main__" や上記の 100 などの文字列定数と数値定数が含まれます。
co_filename、このフィールドの意味は、対応するソース ファイルのファイル名です。
co_firstlineno, このフィールドの意味は、Python ソース ファイルのコードの最初の行に表示される行数です。このフィールドはデバッグ時に非常に重要です。
co_flags、このフィールドの主な意味は、このコード オブジェクトのタイプを識別することです。 0x0080 はこのブロックがコルーチンであることを示し、0x0010 はこのコード オブジェクトがネストされていることを示します。
co_lnotab、このフィールドの意味は主に、各バイトコード命令に対応するソースコード行数を計算するために使用されます。
co_varnames、このフィールドの主な意味は、コード オブジェクト内でローカルに定義された名前を表すことです。
co_names は co_varnames の逆で、ローカルに定義されていないがコード オブジェクトで使用される名前を表します。
co_nlocals、このフィールドは、コード オブジェクト内でローカルに使用される変数の数を示します。
co_stackszie、Python 仮想マシンはスタック コンピューターであるため、このパラメーターの値は、このスタックに必要な最大値を示します。
co_cellvars、co_freevars、これら 2 つのフィールドは主にネストされた関数と関数クロージャに関連しています。このフィールドについては後続の記事で詳しく説明します。
次に、いくつかの実践的な例を使用して、特定のコード オブジェクトを分析します。
import dis import binascii import types d = 10 def test_co01(c): a = 1 b = 2 return a + b + c + d
前の記事では、関数にはコード オブジェクト オブジェクトが含まれると述べましたが、test_co01 (完全なコードについては co01 を参照) のコード オブジェクト オブジェクトの出力結果は次のとおりです。
code argcount 1 nlocals 3 stacksize 2 flags 0043 0x43 code b'6401007d01006402007d02007c01007c0200177c0000177400001753' 9 0 LOAD_CONST 1 (1) 3 STORE_FAST 1 (a) 10 6 LOAD_CONST 2 (2) 9 STORE_FAST 2 (b) 11 12 LOAD_FAST 1 (a) 15 LOAD_FAST 2 (b) 18 BINARY_ADD 19 LOAD_FAST 0 (c) 22 BINARY_ADD 23 LOAD_GLOBAL 0 (d) 26 BINARY_ADD 27 RETURN_VALUE consts None 1 2 names ('d',) varnames ('c', 'a', 'b') freevars () cellvars () filename '/tmp/pycharm_project_396/co01.py' name 'test_co01' firstlineno 8 lnotab b'000106010601'
flags フィールドと上記の各マクロ定義の間で & 演算が実行され、結果が 0 より大きい場合、対応する条件が満たされていることを意味します。
上記のマクロ定義の意味は次のとおりです:
、このフィールドは、関数ローカルを使用してコード オブジェクトが最適化されていることを示します。定義された変数。
、このフィールドの意味は、このコード オブジェクトのコードが実行されると、スタック内の f_locals オブジェクトの dict オブジェクトが作成されることです。フレーム。
は、このコード オブジェクトに位置パラメータが含まれるかどうかを示します。
は、このコード オブジェクトにキーワード パラメーターが含まれているかどうかを示します。
、このコード オブジェクトがネストされた関数であることを示します。
、このコード オブジェクトがジェネレーターであることを示します。
。このコード オブジェクトがコルーチン関数であることを示します。
。コード オブジェクトが反復可能なコルーチン関数であることを示します。
、これは、freevars と cellvars がないこと、つまり関数クロージャーがないことを意味します。
我们使用下面的函数来对这两个字段进行分析:
def test_co02(): a = 1 b = 2 def g(): return a + b return a + b + g()
上面的函数的信息如下所示(完整代码见co02):
code argcount 0 nlocals 1 stacksize 3 flags 0003 0x3 code b'640100890000640200890100870000870100660200640300640400860000' b'7d0000880000880100177c00008300001753' 15 0 LOAD_CONST 1 (1) 3 STORE_DEREF 0 (a) 16 6 LOAD_CONST 2 (2) 9 STORE_DEREF 1 (b) 18 12 LOAD_CLOSURE 0 (a) 15 LOAD_CLOSURE 1 (b) 18 BUILD_TUPLE 2 21 LOAD_CONST 3 () 24 LOAD_CONST 4 ('test_co02..g') 27 MAKE_CLOSURE 0 30 STORE_FAST 0 (g) 20 33 LOAD_DEREF 0 (a) 36 LOAD_DEREF 1 (b) 39 BINARY_ADD 40 LOAD_FAST 0 (g) 43 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 46 BINARY_ADD 47 RETURN_VALUE consts None 1 2 code argcount 0 nlocals 0 stacksize 2 flags 0013 0x13 code b'8800008801001753' 19 0 LOAD_DEREF 0 (a) 3 LOAD_DEREF 1 (b) 6 BINARY_ADD 7 RETURN_VALUE consts None names () varnames () freevars ('a', 'b') cellvars () filename '/tmp/pycharm_project_396/co01.py' name 'g' firstlineno 18 lnotab b'0001' 'test_co02..g' names () varnames ('g',) freevars () cellvars ('a', 'b') filename '/tmp/pycharm_project_396/co01.py' name 'test_co02' firstlineno 14 lnotab b'0001060106021502'
从上面的输出我们可以看到的是,函数 test_co02 的 cellvars 为 ('a', 'b'),函数 g 的 freevars 为 ('a', 'b'),cellvars 表示在其他函数当中会使用本地定义的变量,freevars 表示本地会使用其他函数定义的变量。
再来分析一下函数 test_co02 的 flags,他的 flags 等于 0x3 因为有闭包的存在因此 flags 不会存在 CO_NOFREE,也就是少了值 0x0040 。
这个字段存储的是在函数在被虚拟机执行的时候所需要的最大的栈空间的大小,这也是一种优化手段,因为在知道所需要的最大的栈空间,所以可以在函数执行的时候直接分配指定大小的空间不需要在函数执行的时候再去重新扩容。
def test_stack(): a = 1 b = 2 return a + b
上面的代码相关字节码等信息如下所示:
code argcount 0 nlocals 2 stacksize 2 flags 0043 0x43 code b'6401007d00006402007d01007c00007c01001753' # 字节码指令 # 字节码指令参数 # 参数对应的值 24 0 LOAD_CONST 1 (1) 3 STORE_FAST 0 (a) 25 6 LOAD_CONST 2 (2) 9 STORE_FAST 1 (b) 26 12 LOAD_FAST 0 (a) 15 LOAD_FAST 1 (b) 18 BINARY_ADD 19 RETURN_VALUE consts None # 下标等于 0 的常量 1 # 下标等于 1 的常量 2 # 下标等于 2 的常量 names () varnames ('a', 'b') freevars () cellvars ()
我们现在来模拟一下执行过程,在模拟之前我们首先来了解一下上面几条字节码的作用:
LOAD_CONST,将常量表当中的下标等于 i 个对象加载到栈当中,对应上面的代码 LOAD_CONST 的参数 i = 1。因此加载测常量等于 1 。因此现在栈空间如下所示:
STORE_FAST,将栈顶元素弹出并且保存到 co_varnames 对应的下标当中,根据上面的字节码参数等于 0 ,因此将 1 保存到 co_varnames[0] 对应的对象当中。
LOAD_CONST,将下标等于 2 的常量加载进入栈中。
STORE_FAST,将栈顶元素弹出,并且保存到 varnames 下标为 1 的对象。
LOAD_FAST,是取出 co_varnames 对应下标的数据,并且将其压入栈中。我们直接连续执行两个 LOAD_FAST 之后栈空间的布局如下:
BINARY_ADD,这个字节码指令是将栈空间的两个栈顶元素弹出,然后将两个数据进行相加操作,然后将相加得到的结果重新压入栈中。
RETURN_VALUE,将栈顶元素弹出并且作为返回值返回。
从上面的整个执行过程来看整个栈空间使用的最大的空间长度为 2 ,因此 stacksize = 2 。
以上がPython 仮想マシンにおけるコード オブジェクトの役割は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。