Heim  >  Artikel  >  Backend-Entwicklung  >  So implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine

So implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine

WBOY
WBOYnach vorne
2023-05-04 08:31:06857Durchsuche

Python Common Bytecode

LOAD_CONST

Diese Anweisung wird verwendet, um eine Konstante in den Stapel zu laden. Konstanten können Objekte wie Zahlen, Zeichenfolgen, Tupel, Listen, Wörterbücher usw. sein. Zum Beispiel:

>>> dis.dis(lambda: 42)
  1           0 LOAD_CONST               1 (42)
              2 RETURN_VALUE

LOAD_NAME

Diese Anweisung wird verwendet, um eine Variable in den Stapel zu laden. Zum Beispiel:

>>> dis.dis(lambda: x)
  1           0 LOAD_GLOBAL              0 (x)
              2 RETURN_VALUE
>>>

STORE_NAME

Diese Anweisung wird verwendet, um den Wert oben im Stapel in einer Variablen zu speichern. Zum Beispiel:

>>> dis.dis("x=42")
  1           0 LOAD_CONST               0 (42)
              2 STORE_NAME               0 (x)
              4 LOAD_CONST               1 (None)
              6 RETURN_VALUE

BINARY_ADD

Diese Anweisung wird verwendet, um die beiden Werte oben auf dem Stapel zu addieren und das Ergebnis auf den Stapel zu verschieben.

>>> dis.dis(lambda: x + y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE

BINARY_SUBTRACT

Diese Anweisung wird verwendet, um die beiden Werte oben auf dem Stapel zu subtrahieren und das Ergebnis auf den Stapel zu verschieben.

>>> dis.dis(lambda: x - y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_SUBTRACT
              6 RETURN_VALUE

Der gleiche Bytecode für Addition, Subtraktion, Multiplikation und Division, um den Rest zu erhalten, lautet wie folgt:

>>> dis.dis(lambda: x + y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> dis.dis(lambda: x - y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_SUBTRACT
              6 RETURN_VALUE
>>> dis.dis(lambda: x * y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_MULTIPLY
              6 RETURN_VALUE
>>> dis.dis(lambda: x / y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_TRUE_DIVIDE
              6 RETURN_VALUE
>>> dis.dis(lambda: x // y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_FLOOR_DIVIDE
              6 RETURN_VALUE
>>> dis.dis(lambda: x % y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_MODULO
              6 RETURN_VALUE

COMPARE_OP

Diese Anweisung wird verwendet, um die beiden Werte oben auf dem Stapel zu vergleichen und das Ergebnis zu übertragen Der Vergleich auf dem Stapel. Der Parameter des letzten Bytes nach dem Code stellt das Vergleichssymbol für kleiner als, größer als, ungleich usw. dar. Zum Beispiel:

>>> dis.dis(lambda: x - y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 BINARY_SUBTRACT
              6 RETURN_VALUE
>>> dis.dis(lambda: x > y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               4 (>)
              6 RETURN_VALUE
>>> dis.dis(lambda: x < y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               0 (<)
              6 RETURN_VALUE
>>> dis.dis(lambda: x != y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               3 (!=)
              6 RETURN_VALUE
>>> dis.dis(lambda: x <= y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               1 (<=)
              6 RETURN_VALUE
>>> dis.dis(lambda: x >= y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               5 (>=)
              6 RETURN_VALUE
>>> dis.dis(lambda: x == y)
  1           0 LOAD_GLOBAL              0 (x)
              2 LOAD_GLOBAL              1 (y)
              4 COMPARE_OP               2 (==)
              6 RETURN_VALUE

RETURN_VALUE

Legen Sie das oberste Element des Stapels als Rückgabewert fest.

BUILD_LIST

Mit diesem Befehl wird eine Liste erstellt. Zum Beispiel:

>>> dis.dis(lambda: [a, b, c, e])
  1           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 LOAD_GLOBAL              2 (c)
              6 LOAD_GLOBAL              3 (e)
              8 BUILD_LIST               4
             10 RETURN_VALUE

Dieser Bytecode-Befehl hat einen Parameter, der die Anzahl der Listenelemente im Stapelbereich angibt. Im obigen Beispiel ist dieser Parameter 4.

BUILD_TUPLE

Mit diesem Befehl wird ein Tupel erstellt. Zum Beispiel:

>>> dis.dis(lambda: (a, b, c))
  1           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 LOAD_GLOBAL              2 (c)
              6 BUILD_TUPLE              3
              8 RETURN_VALUE

Derselbe Bytecode verfügt auch über einen Parameter, der die Anzahl der Elemente zum Erstellen eines Tupels angibt.

BUILD_MAP

Mit diesem Befehl wird ein Wörterbuch erstellt. Zum Beispiel:

BUILD_SET

Diese Anweisung wird wie eine Liste und ein Tupel zum Erstellen eines Sammlungsobjekts verwendet. Dieselbe Anweisung verfügt auch über einen Parameter, der die Anzahl der zum Erstellen der Sammlung verwendeten Elemente angibt.

>>> dis.dis(lambda: {a, b, c, d})
  1           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 LOAD_GLOBAL              2 (c)
              6 LOAD_GLOBAL              3 (d)
              8 BUILD_SET                4
             10 RETURN_VALUE

BUILD_CONST_KEY_MAP

Dieser Befehl wird zum Erstellen eines Wörterbuchobjekts verwendet. Derselbe Befehl verfügt auch über einen Parameter, der die Anzahl der Elemente im Wörterbuch angibt.

>>> dis.dis(lambda: {1:2, 3:4})
  1           0 LOAD_CONST               1 (2)
              2 LOAD_CONST               2 (4)
              4 LOAD_CONST               3 ((1, 3))
              6 BUILD_CONST_KEY_MAP      2
              8 RETURN_VALUE

Analyse des Prinzips von Dekoratoren aus Bytecode-Perspektive

Wenn Sie ein Pythoner sind, dann haben Sie sicher schon von Dekoratoren gehört. Dies ist ein syntaktischer Zucker für Python. Wir können damit viele interessante Dinge tun. B. das Hinzufügen einiger Funktionen zur Funktion, ohne den Quellcode zu ändern, z. B. die Berechnung der Zeit.

import time
 
def eval_time(func):
    
    def cal_time(*args, **kwargs):
        start = time.time()
        r = func(*args, **kwargs)
        end = time.time()
        return r, end - start
    return cal_time
 
 
@eval_time
def fib(n):
    a = 0
    b = 1
    while n > 0:
        n -= 1
        a, b = b, a + b
    return a

Im obigen Code haben wir eine Funktion zur Berechnung der Fibonacci-Folge implementiert. Außerdem haben wir eine eval_time-Funktion geschrieben, um die Ausführungszeit der Funktion zu berechnen. Rufen Sie nun die Funktion fib(10) auf, die Ausgabe des Programms Wie unten gezeigt:

>>>fib(10)
(55, 5.9604644775390625e-06)

Sie können sehen, dass der gewünschte Effekt erzielt wird.

Jetzt verwenden wir ein einfacheres Beispiel, um die obige Codestruktur zu simulieren, damit wir den Ausführungsprozess der obigen Funktion analysieren können:

s = """
def decorator(func):
    print("Hello")
    return func
 
@decorator
def fib(n):
    pass
"""
dis.dis(s)

Der Bytecode, der der Ausgabe der obigen dis-Funktion entspricht, lautet wie folgt:

  2           0 LOAD_CONST               0 (“, Zeile 2>)<br>              2. LOAD _CONST               1 ('Dekorateur')<br>              4 MAKE_FUNCTION            0<br>              6 STORE_NAME             0 (Dekorateur)<br> <br>  6           8 LOAD_NAME               0 (Dekorator)<br> <br>  7          10 LOAD_CONST               2 (<code-objekt-fib bei datei>", Zeile 6>)<br>           12 LOAD_CONST               3 ('fib')<br>             14 MAKE_FUNCTION            0<br>           16 CALL_FUNCTION            1<br>             18 STORE_NAME               1 (fib)<br> 20 LOAD_CONST               4 (Keine)<br>             22 RETURN_VALUE<br> <br>Disassembly von <code object decorator bei datei>“, Zeile 2>: <br>  3           0 LOAD_GLOBAL              0 (drucken)<br>              2 LOAD_CONST               1 ('Hallo')<br>            4 CALL_FUNCTION            1<br>             6 POP_TOP<br> <br>  4           8 LOAD_FAST                0 (func)<br>             10 RETURN_VALUE# 🎜🎜# <br>Disassemblierung von <codeobjekt-fib bei datei>“, Zeile 6>:<br>  8           0 LOAD_CONST               0 #              2 RETURN_VALUE<br> <br></codeobjekt-fib>

执行第一条指令 LOAD_CONST,这个对象里面主要是包含函数 Dekorateur 的字节码,主要是上面字节码的第二块内容。在执行完这条字节码之后栈空间如下所示:

So implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine执行完第二Mit LOAD_CONST können Sie einen Decorator verwenden加载进入栈空间当中.

So implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine执行第三条指令. MAKE_FUNCTION,这条字节码的作用是在虚拟机内部创建一个函数, 函数称为 decorator, 函数对应的字节码则是在先前压入栈空间当中的 code object函数对象压入栈中.

# 🎜🎜#

STORE_NAME的字节码当中 co_names[oparg] 就是 decorator 。# 🎜🎜#So implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine

LOAD_NAME新加载进入栈空间当中,也就是上面的 Dekorateur LOAD_CONST,LOAD_CON ST und MAKE_FUNCTION werden verwendet, um eine neue Funktion zu erhalten空间如下所示:

So implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine

接下来的一条指令非常重要,这条指令便是装饰器的核心原理,CALL_FUNCTION 这条指令有一个参数 i,在上面的字节码当中为 1,也就是说从栈顶开始的前 i 个元素都是函数参数,调用的函数在栈空间的位置为 i + 1 (从栈顶往下数),那么在上面的情况下就是说调用 decorator 函数,并且将 fib 函数作为 decorator 函数的参数,decorator 函数的返回值再压入栈顶。在上面的代码当中 decorator 函数返回值也是一个函数,也就是 decorator 函数的参数,即 fib 函数。

So implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine

接下来便是 STORE_NAME 字节码,这条字节码的含义我们在前面已经说过了,就是将栈顶元素弹出,保存到 co_names[oparg] 指向的对象当中,在上面的代码当中也就是将栈顶的对象保存到 fib 当中。栈顶元素 fib 函数是调用函数 decorator 的返回值。

看到这里就能够理解了原来装饰器的最根本的原理不就是函数调用嘛,比如我们最前面的用于计算函数执行时间的装饰器的原理就是:

fib = eval_time(fib)

将 fib 函数作为 eval_time 函数的参数,再将这个函数的返回值保存到 fib 当中,当然这个对象必须是可调用的,不然后面使用 fib() 就会保存,我们可以使用下面的代码来验证这个效果。

def decorator(func):
    return func()
 
 
@decorator
def demo():
    return "function demo return string : Demo"
 
print(demo)

执行上面的程序结果为:

function demo return string : Demo

可以看到 demo 已经变成了一个字符串对象而不再是一个函数了,因为 demo = decorator(demo),而在函数 decorator 当中返回值是 demo 函数自己的返回值,因此才打印了字符串。

Das obige ist der detaillierte Inhalt vonSo implementieren Sie den Dekorator des Bytecodes der virtuellen Python-Maschine. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen