Quel est le rôle de l'objet Code dans la machine virtuelle Python ?

PHPz
Libérer: 2023-05-10 17:46:06
avant
953 Les gens l'ont consulté

Structure des données de l'objet code

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;
Copier après la connexion

Voici le rôle de chaque champ dans l'objet code :

  • Tout d'abord, vous devez comprendre le concept de bloc de code. Le soi-disant bloc de code est un petit code python. , qui est exécuté comme une petite unité dans son ensemble. Les blocs de code courants en Python incluent : le corps de la fonction, la définition de classe et un module.

  • argcount, cela représente le nombre de paramètres dans un bloc de code. Ce paramètre n'est utile que pour les blocs de code du corps de fonction, car la fonction peut avoir des paramètres. Par exemple, le pycdemo.py ci-dessus est un module plutôt qu'une fonction. , donc ce paramètre La valeur correspondante est 0.

  • co_code, le contenu spécifique de cet objet est une séquence d'octets, qui stocke le vrai bytecode python. Il est principalement utilisé pour l'exécution de la machine virtuelle python et ne sera pas analysé en détail dans cet article.

  • co_consts, ce champ est un champ de type liste, contenant principalement des constantes de chaîne et des constantes numériques, telles que "__main__" et 100 ci-dessus.

  • co_filename, la signification de ce champ est le nom du fichier source correspondant.

  • co_firstlineno, la signification de ce champ est le nombre de lignes qui apparaît dans la première ligne de code du fichier source python. Ce champ est très important lors du débogage.

  • co_flags, la signification principale de ce champ est d'identifier le type de cet objet code. 0x0080 indique que ce bloc est une coroutine, 0x0010 indique que cet objet de code est imbriqué, et ainsi de suite.

  • co_lnotab, la signification de ce champ sert principalement à calculer le nombre de lignes de code source correspondant à chaque instruction de bytecode.

  • co_varnames, la signification principale de ce champ est de représenter un nom défini localement dans un objet code.

  • co_names, à l'opposé de co_varnames, représente des noms qui ne sont pas définis localement mais utilisés dans l'objet code.

  • co_nlocals, ce champ indique le nombre de variables utilisées localement dans un objet code.

  • co_stackszie, parce que la machine virtuelle python est un ordinateur à pile, la valeur de ce paramètre indique la valeur maximale requise pour cette pile.

  • co_cellvars, co_freevars, ces deux champs sont principalement liés aux fonctions imbriquées et aux fermetures de fonctions Nous expliquerons ce champ en détail dans les articles suivants.

Analyse détaillée de CodeObject

Nous utilisons maintenant quelques exemples pratiques pour analyser des objets de code spécifiques.

import dis
import binascii
import types

d = 10


def test_co01(c):
    a = 1
    b = 2
    return a + b + c + d
Copier après la connexion

Dans l'article précédent, nous avons mentionné qu'une fonction inclut un objet code objet. Le résultat de sortie de l'objet code objet de test_co01 (voir co01 pour le code complet) est le suivant :

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;
Copier après la connexion
  • La valeur du champ. argcount est égal à 1 , indiquant que la fonction a un paramètre et que cette fonction test_co01 a un paramètre c qui se correspondent.

  • La valeur du champ nlocals est égale à 3, indiquant qu'un total de trois variables locales de fonction a, b, c sont implémentées dans la fonction test_co01.

  • Les noms de champs correspondent aux co_names dans le code. D'après la définition précédente, la variable globale d est utilisée dans la fonction test_co01, mais elle n'est pas définie dans la fonction.

  • Field varnames, cela représente les variables utilisées dans les définitions locales. Il y a trois variables principales a, b, c dans la fonction test_co01.

  • Le champ filename est l'adresse du fichier python.

  • Le champ firstlineno indique que la première ligne de la fonction apparaît à la ligne 8 du code python correspondant.

Analyse détaillée du champ Flags

Nous utilisons spécifiquement le code source de python3.5 pour l'analyse. L'implémentation spécifique de la machine virtuelle cpython est la suivante (Include/code.h) :

/* Masks for co_flags above */
#define CO_OPTIMIZED	0x0001
#define CO_NEWLOCALS	0x0002
#define CO_VARARGS	0x0004
#define CO_VARKEYWORDS	0x0008
#define CO_NESTED       0x0010
#define CO_GENERATOR    0x0020
/* The CO_NOFREE flag is set if there are no free or cell variables.
   This information is redundant, but it allows a single flag test
   to determine whether there is any extra work to be done when the
   call frame it setup.
*/
#define CO_NOFREE       0x0040

/* The CO_COROUTINE flag is set for coroutine functions (defined with
   ``async def`` keywords) */
#define CO_COROUTINE            0x0080
#define CO_ITERABLE_COROUTINE   0x0100
Copier après la connexion

Si le champ flags et. chacun des éléments ci-dessus La définition de la macro effectue l'opération & Si le résultat obtenu est supérieur à 0, cela signifie que les conditions correspondantes sont remplies.

La signification de la définition de macro ci-dessus est la suivante :

  • CO_OPTIMIZED Ce champ indique que l'objet code est optimisé et utilise des variables définies localement par la fonction.

  • CO_NEWLOCALS, la signification de ce champ est que lorsque le code de cet objet code est exécuté, un objet dict sera créé pour l'objet f_locals dans le cadre de pile.

  • CO_VARARGS, indiquant si cet objet de code contient des paramètres de position.

  • CO_VARKEYWORDS, indiquant si cet objet de code contient des paramètres de mots-clés.

  • CO_NESTED, indiquant que cet objet code est une fonction imbriquée.

  • CO_GENERATOR, indiquant que cet objet de code est un générateur.

  • CO_COROUTINE, indiquant que cet objet de code est une fonction coroutine.

  • CO_ITERABLE_COROUTINE, indiquant que l'objet code est une fonction coroutine itérable.

  • CO_NOFREE, cela signifie qu'il n'y a pas de freevars ni de cellvars, c'est-à-dire qu'il n'y a pas de fermeture de fonction.

Analysons maintenant les drapeaux de la fonction précédente test_co01. Sa valeur correspondante est égale à 0x43, ce qui signifie que cette fonction satisfait trois caractéristiques : CO_NEWLOCALS, CO_OPTIMIZED et CO_NOFREE.

freevars & cellvars

我们使用下面的函数来对这两个字段进行分析:

def test_co02():
    a = 1
    b = 2

    def g():
        return a + b
    return a + b + g()
Copier après la connexion

上面的函数的信息如下所示(完整代码见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;
Copier après la connexion

从上面的输出我们可以看到的是,函数 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
Copier après la connexion

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

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 ()
Copier après la connexion

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

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

Quel est le rôle de lobjet Code dans la machine virtuelle Python ?

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

Quel est le rôle de lobjet Code dans la machine virtuelle Python ?

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

Quel est le rôle de lobjet Code dans la machine virtuelle Python ?

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

Quel est le rôle de lobjet Code dans la machine virtuelle Python ?

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

Quel est le rôle de lobjet Code dans la machine virtuelle Python ?

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

Quel est le rôle de lobjet Code dans la machine virtuelle Python ?

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

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal