ホームページ  >  記事  >  バックエンド開発  >  Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

PHPz
PHPz転載
2023-05-10 17:46:06930ブラウズ

コード オブジェクトのデータ構造

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 を参照) のコード オブジェクト オブジェクトの出力結果は次のとおりです。

    #フィールド argcount の値は 1 に等しく、関数が 1 つのパラメーターを持つことを示します。この関数 test_co01 には、互いに対応するパラメーター c があります。
  • フィールド nlocals の値は 3 に等しく、合計 3 つの関数ローカル変数 a、b、c が関数 test_co01 に実装されていることを示します。
  • フィールド名はコード内の co_names に対応します。前の定義によれば、グローバル変数 d は関数 test_co01 で使用されていますが、関数内では定義されていません。
  • フィールド変数名、これはローカル定義で使用される変数を表します。関数 test_co01 には 3 つの主要な変数 a、b、c があります。
  • フィールド filename は、Python ファイルのアドレスです。
  • フィールド firstlineno は、関数の最初の行が、対応する Python コードの 8 行目に現れることを示します。
  • Flags フィールドの詳細な分析

分析には特に python3.5 のソース コードを使用します。cpython 仮想マシンの具体的な実装は次のとおりです (インクルード/コード) .h ):

code
   argcount 1
   nlocals 3
   stacksize 2
   flags 0043 0x43
   code b&#39;6401007d01006402007d02007c01007c0200177c0000177400001753&#39;
  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 (&#39;d&#39;,)
   varnames (&#39;c&#39;, &#39;a&#39;, &#39;b&#39;)
   freevars ()
   cellvars ()
   filename &#39;/tmp/pycharm_project_396/co01.py&#39;
   name &#39;test_co01&#39;
   firstlineno 8
   lnotab b&#39;000106010601&#39;

flags フィールドと上記の各マクロ定義の間で & 演算が実行され、結果が 0 より大きい場合、対応する条件が満たされていることを意味します。

上記のマクロ定義の意味は次のとおりです:

  • CO_OPTIMIZED

    、このフィールドは、関数ローカルを使用してコード オブジェクトが最適化されていることを示します。定義された変数。

  • CO_NEWLOCALS

    、このフィールドの意味は、このコード オブジェクトのコードが実行されると、スタック内の f_locals オブジェクトの dict オブジェクトが作成されることです。フレーム。

  • CO_VARARGS

    は、このコード オブジェクトに位置パラメータが含まれるかどうかを示します。

  • CO_VARKEYWORDS

    は、このコード オブジェクトにキーワード パラメーターが含まれているかどうかを示します。

  • CO_NESTED

    、このコード オブジェクトがネストされた関数であることを示します。

  • CO_GENERATOR

    、このコード オブジェクトがジェネレーターであることを示します。

  • CO_COROUTINE

    。このコード オブジェクトがコルーチン関数であることを示します。

  • CO_ITERABLE_COROUTINE

    。コード オブジェクトが反復可能なコルーチン関数であることを示します。

  • CO_NOFREE

    、これは、freevars と cellvars がないこと、つまり関数クロージャーがないことを意味します。

  • 次に、前の関数 test_co01 のフラグを分析しましょう。その対応する値は 0x43 に等しく、これは、この関数が 3 つの特性 (CO_NEWLOCALS、CO_​​OPTIMIZED、CO_NOFREE) を満たすことを意味します。

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&#39;640100890000640200890100870000870100660200640300640400860000&#39;
      b&#39;7d0000880000880100177c00008300001753&#39;
 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 (<code object g at 0x7f133ff496f0, file "/tmp/pycharm_project_396/co01.py", line 18>)
             24 LOAD_CONST               4 (&#39;test_co02.<locals>.g&#39;)
             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&#39;8800008801001753&#39;
 19           0 LOAD_DEREF               0 (a)
              3 LOAD_DEREF               1 (b)
              6 BINARY_ADD
              7 RETURN_VALUE
         consts
            None
         names ()
         varnames ()
         freevars (&#39;a&#39;, &#39;b&#39;)
         cellvars ()
         filename &#39;/tmp/pycharm_project_396/co01.py&#39;
         name &#39;g&#39;
         firstlineno 18
         lnotab b&#39;0001&#39;
      &#39;test_co02.<locals>.g&#39;
   names ()
   varnames (&#39;g&#39;,)
   freevars ()
   cellvars (&#39;a&#39;, &#39;b&#39;)
   filename &#39;/tmp/pycharm_project_396/co01.py&#39;
   name &#39;test_co02&#39;
   firstlineno 14
   lnotab b&#39;0001060106021502&#39;

从上面的输出我们可以看到的是,函数 test_co02 的 cellvars 为 ('a', 'b'),函数 g 的 freevars 为 ('a', 'b'),cellvars 表示在其他函数当中会使用本地定义的变量,freevars 表示本地会使用其他函数定义的变量。

再来分析一下函数 test_co02 的 flags,他的 flags 等于 0x3 因为有闭包的存在因此 flags 不会存在 CO_NOFREE,也就是少了值 0x0040 。

stacksize

这个字段存储的是在函数在被虚拟机执行的时候所需要的最大的栈空间的大小,这也是一种优化手段,因为在知道所需要的最大的栈空间,所以可以在函数执行的时候直接分配指定大小的空间不需要在函数执行的时候再去重新扩容。

def test_stack():
    a = 1
    b = 2
    return a + b

上面的代码相关字节码等信息如下所示:

code
   argcount 0
   nlocals 2
   stacksize 2
   flags 0043 0x43
   code b&#39;6401007d00006402007d01007c00007c01001753&#39;
   #					  字节码指令		 # 字节码指令参数 # 参数对应的值
 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 (&#39;a&#39;, &#39;b&#39;)
   freevars ()
   cellvars ()

我们现在来模拟一下执行过程,在模拟之前我们首先来了解一下上面几条字节码的作用:

LOAD_CONST,将常量表当中的下标等于 i 个对象加载到栈当中,对应上面的代码 LOAD_CONST 的参数 i = 1。因此加载测常量等于 1 。因此现在栈空间如下所示:

Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

STORE_FAST,将栈顶元素弹出并且保存到 co_varnames 对应的下标当中,根据上面的字节码参数等于 0 ,因此将 1 保存到 co_varnames[0] 对应的对象当中。

Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

LOAD_CONST,将下标等于 2 的常量加载进入栈中。

Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

STORE_FAST,将栈顶元素弹出,并且保存到 varnames 下标为 1 的对象。

Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

LOAD_FAST,是取出 co_varnames 对应下标的数据,并且将其压入栈中。我们直接连续执行两个 LOAD_FAST 之后栈空间的布局如下:

Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

BINARY_ADD,这个字节码指令是将栈空间的两个栈顶元素弹出,然后将两个数据进行相加操作,然后将相加得到的结果重新压入栈中。

Python 仮想マシンにおけるコード オブジェクトの役割は何ですか?

RETURN_VALUE,将栈顶元素弹出并且作为返回值返回。

从上面的整个执行过程来看整个栈空间使用的最大的空间长度为 2 ,因此 stacksize = 2 。

以上がPython 仮想マシンにおけるコード オブジェクトの役割は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。