Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erklärung der Python-Dekoratoren

Detaillierte Erklärung der Python-Dekoratoren

高洛峰
高洛峰Original
2016-12-01 15:55:172191Durchsuche

Dekoratoren werden in Python sehr häufig verwendet. Wir können einige spezifische Methoden und allgemeine Methoden als Dekoratoren schreiben. Dies bietet eine große Bequemlichkeit beim Aufrufen dieser Methoden, was die Lesbarkeit, Einfachheit und Skalierbarkeit unseres Codes verbessert.

Bevor wir Python-Dekoratoren lernen, werfen wir einen Blick auf dieses Beispiel:

1. Geltungsbereich

# coding:utf-8
 
msg = 'hello test1'
 
 
def add():
    msg = 'this is add'
    print msg     #当执行add()时将打印'this is add'
def add2():
    print msg     #当执行add2()时将打印'hello test1'

Das obige Beispiel gilt einfach für den Geltungsbereich von Python. Hier ist eine Erklärung. Im Beispiel wird die globale Variable msg deklariert, und in der Add-Funktion wird auch eine lokale Variable msg deklariert. Wenn „print msg“ von add() ausgeführt wird, wird zunächst ermittelt, ob in add eine lokale Variable msg vorhanden ist Wenn die Funktion nicht gefunden wird, gehen Sie zu Finden Sie, ob die Variable im oberen Bereich vorhanden ist. Wenn die Funktion endet, werden auch die lokalen Variablen vertieft:

2. Schließung

# coding:utf-8
 
def add():
    msg = 'hello add'
    def inner():
        print msg               #运行add()这里会打印'hello add'
    return inner

>>> obj = add()
>>> obj            #可见obj是一个指向inner函数的对象
<function inner at 0x0000000002855438>
...
>>> obj()
hello add           #运行obj()打印msg

Wenn Sie nach dem Lesen des obigen Beispiels Zweifel haben, obj ist das Objekt, das auf die innere Funktion zeigt. Wenn obj verwendet wird, entspricht dies der Ausführung von inner, aber die Add-Funktion wird nicht ausgeführt, was bedeutet, dass msg nicht deklariert wird und inner die Variable msg nicht deklariert Finden Sie den Wert der Variablen msg?

Dies ist der „Abschluss“ in Python. In menschlichen Begriffen bedeutet dies, dass Funktionen, die in einem nicht-globalen Bereich verschachtelt sind, sich daran erinnern können, dass sie definiert sind In. Dies kann durch einen Blick auf das obj.func_closure-Attribut der Funktion geschlossen werden. Dieses Attribut enthält den Wert im geschlossenen Bereich (nur der erfasste Wert wird eingeschlossen). Wenn andere Werte in add definiert sind, ist der geschlossene Bereich Nein) Schließung ist das Kernprinzip von Python-Dekoratoren. Als nächstes schreiben wir ein einfaches Dekorator-Beispiel:

3. Einfacher Dekorator

# coding:utf-8
 
def check_args_num(func):
    # 该装饰器用于检查传入的参数数量,检查是否是两个参数
    def inner(*args, **kwargs):
        args_list = list(args)
        if len(args_list) < 2:
 
            for i in range(2 - len(args)):
                # 如果参数数量小于2则用0填补
                args_list.append(0)
        if len(args_list) > 2:
            # 如果参数数量大于2则打印错误
            print &#39;The args number is too many!&#39;
        func(*args_list, **kwargs)
    return inner
 
 
@check_args_num
def add(x, y):
    return x + y

Ausführungsergebnisse:

>>>print add(1,2)
3
...
>>>print add(100)
100
...
>>>print add(1,2,3)
Traceback (most recent call last):
  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py", line 3, in Exec
    exec exp in global_vars, local_vars
  File "<input>", line 1, in <module>
  File "E:/code/my_project/decorator/test1.py", line 14, in inner
    raise Exception(&#39;The args number is too many!&#39;)
Exception: The args number is too many!
...
>>>add
<function inner at 0x0000000002A6C3C8>
#可以看到add函数现在指向的是inner

4. Mehrere Dekorateure

# coding:utf-8
 
def check_args_int(func):
    # 该装饰器用于检查传入的参数是否是int型
    def ensure_int(*args, **kwargs):
        from array import array
        try:
            array(&#39;H&#39;, args)
        except Exception, e:
            raise Exception(e)
        return func(*args, **kwargs)
 
    return ensure_int
 
 
def check_args_num(func):
    # 该装饰器用于检查传入的参数数量,检查是否是两个参数
    def inner(*args, **kwargs):
        args_list = list(args)
        if len(args_list) < 2:
 
            for i in range(2 - len(args)):
                # 如果参数数量小于2则用0填补
                args_list.append(0)
        if len(args_list) > 2:
            # 如果参数数量大于2则打印错误
            raise Exception(&#39;The args number is too many!&#39;)
        return func(*args_list, **kwargs)
 
    return inner
 
 
@check_args_num
@check_args_int
def add(x, y):
    return x + y

Hier wird ein zusätzlicher Dekorator für die Parameterinhaltsprüfung hinzugefügt. Wenn mehrere Dekoratoren vorhanden sind, wird zuerst Check_args_num und dann check_args_int ausgeführt Sie können sehen, dass add immer noch auf inner verweist. Wenn mehrere Dekoratoren vorhanden sind und add aufgerufen wird, ist der Aufrufeintrag immer der erste Dekorator. Führen Sie dann den nächsten aus und übergeben Sie die Parameter Sequenz

>>> print add(1,&#39;fsaf&#39;)
Traceback (most recent call last):
  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py", line 3, in Exec
    exec exp in global_vars, local_vars
  File "<input>", line 1, in <module>
  File "E:/code/my_project/decorator/test1.py", line 28, in inner
    return func(*args_list, **kwargs)
  File "E:/code/my_project/decorator/test1.py", line 10, in ensure_int
    raise Exception(e)
Exception: an integer is required
...
>>> add
<function inner at 0x0000000002B1C4A8>

5. Dekoratoren mit Parametern

Wir wissen, dass bei der Definition eines Dekorators der erste Parameter, der an den Dekorator übergeben wird, die dekorierte Funktion ist (z. B. im Beispiel hinzufügen). Manchmal müssen wir auch zusätzliche Parameter an den Dekorateur übergeben, wie folgt:

# coding:utf-8
 
def check_args_int(func):
    # 该装饰器用于检查传入的参数是否是int型
    def ensure_int(*args, **kwargs):
        from array import array
        try:
            array(&#39;H&#39;, args)
        except Exception, e:
            raise Exception(e)
        return func(*args, **kwargs)
 
    return ensure_int
 
 
def check_args_num(flag):
    &#39;&#39;&#39;
    :param func: 被装饰函数
    :param flag: 决定是否检查参数数量
    &#39;&#39;&#39;
 
    # 该装饰器用于检查传入的参数数量,检查是否是两个参数
    def get_func(func):
 
        def inner(*args, **kwargs):
            if flag == &#39;false&#39;:
                print &#39;Skip check !&#39;
                return func(*args, **kwargs)
            args_list = list(args)
            if len(args_list) < 2:
 
                for i in range(2 - len(args)):
                    # 如果参数数量小于2则用0填补
                    args_list.append(0)
            if len(args_list) > 2:
                # 如果参数数量大于2则打印错误
                raise Exception(&#39;The args number is too many!&#39;)
            return func(*args_list, **kwargs)
 
        return inner
 
    return get_func
 
 
@check_args_num(&#39;false&#39;)
@check_args_int
def add(x, y):
    return x + y

In diesem Beispiel unterscheidet sich nur check_args_num vom vorherigen. Der Unterschied besteht darin, dass der Dekorator check_args_num über ein zusätzliches Parameterflag verfügt. Wenn flag=='false', überspringen Sie die Parameternummernprüfung, das Folgende ist das Ausgabeergebnis 🎜>

>>>print add(1, 2)
Skip check !
3

6. Decorator-Decorator ohne Parameter

Das Decorator-Modul ist ein Python-Modul, das zur spezifischen Kapselung von Decorators verwendet wird Gleichzeitig bleibt die Signatur der dekorierten Funktion erhalten. Zuvor haben wir über die Implementierung der Python-Dekoration durch Verschlüsse gesprochen das Gleiche


from decorator import decorator
 
 
@decorator
def check_num(func, *args, **kwargs):
    if len(args) != 2:
        raise Exception(&#39;Your args number is not two&#39;)
    else:
        return func(*args, **kwargs)
 
 
@check_num
def add(x, y):
    return x + y

>>> add
<function add at 0x0000000002D43BA8>
>>> print add(1,2)
3
...
>>> add(1,2,3)
Traceback (most recent call last):
  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py", line 3, in Exec
    exec exp in global_vars, local_vars
  File "<input>", line 1, in <module>
TypeError: add() takes exactly 2 arguments (3 given)
 
#可以看到这里当我们传三个参数给add()函数时,他直接从add()函数抛出类型错误异常,
#并没有进入check_num装饰器进行参数校验,可见被装饰的函数add()的签名还是他本身
#如果直接构造装饰器,那么这里将会从check_num里面抛出异常,如下:
 
def check_num(func):
    def inner(*args,**kwargs):
        if len(args) != 2:
            raise Exception(&#39;Your args number is not two&#39;)
        else:
            return func(*args,**kwargs)
    return inner
 
@check_num
def add(x, y):
    return x + y
 
 
>>> add
<function inner at 0x0000000002E233C8>
>>>add(1,2,3)
Traceback (most recent call last):
  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd.py", line 2411, in <module>
    globals = debugger.run(setup[&#39;file&#39;], None, None, is_module)
  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd.py", line 1802, in run
    launch(file, globals, locals)  # execute the script
  File "E:/code/my_project/decorator/test3.py", line 14, in <module>
    print add(1,2,3)
  File "E:/code/my_project/decorator/test3.py", line 4, in inner
    raise Exception(&#39;Your args number is not two&#39;)
Exception: Your args number is not two
Wie aus dem obigen Beispiel ersichtlich ist, erfolgt die Ausführung durch Schließung beim Aufbau eines Dekorators Der Funktionseintrag ist eine verschachtelte Funktion im Dekorator, daher können die oben genannten Probleme auftreten, wenn add(1,2,3) ausgeführt wird, wird die innere Funktion zuerst ausgeführt (wenn im inneren keine Parameterkalibrierung erfolgt). Hier wird keine Ausnahme ausgelöst. Die Funktion add(x,y) wird tatsächlich nur aufgerufen, wenn return func(*args,**kwargs) ausgeführt wird. Nur dann wird eine Ausnahme ausgelöst. Dies führt dazu, dass das Programm redundant ausgeführt wird Code, der Speicher und CPU verschwendet.

7. Dekorator mit Parametern

Was ist, wenn der Dekorator Parameter übernehmen soll?

from decorator import decorator
 
 
def check(flag):
    @decorator
    def check_num(func, *args, **kwargs):
        if flag == &#39;false&#39;:
            print &#39;skip check !&#39;
            return func(*args,**kwargs)
        if len(args) != 2:
            raise Exception(&#39;Your args number is not two&#39;)
        else:
            return func(*args, **kwargs)
    return check_num
 
 
@check(&#39;false&#39;)
def add(x, y):
    return x + y
 
>>>add
<function add at 0x0000000002C53C18>
>>>add(1,2)
skip check !
3
Das war’s für das Decorator-Modul, und es gibt einige nicht erwähnte Funktionen, die auf einen Blick sichtbar sind Der Quellcode. Seine Prinzipien werden alle mithilfe von Python-Verschlüssen implementiert

8. functools.wraps(func) decorator

Die Funktionen von functools.wraps und decorator-Modulen sind die gleichen, beide zur Lösung des Problems Signaturproblem der dekorierten Funktion, hier listen wir nur ein Beispiel mit Parametern für diese Art von Decorator-Konstruktionsmethode auf:

  对比上面通过decorator模块装饰函数的例子,我们可以发现,用decorator装饰函数的代码更加简洁易懂,但是他们二者的执行效率谁更高呢?下面我们通过Timer来测试下:

from timeit import Timer
 
print Timer(&#39;add(1,2)&#39;,setup=&#39;from __main__ import add&#39;).timeit(100000)
 
#将该段代码 加在 之前的例子中
#这里打印的是运行100000次的时间

   

  functools.wraps装饰执行结果:

2.37299322602

   

  decorator模块装饰执行结果:

3.42141566059

   

  执行效率wraps略高,但是这里是执行了10万次他们之间的差距约为1秒,所以我个人还是比较青睐于用decorator模块装饰函数,毕竟看起来易懂,写法也较为简单!本文就将装饰器介绍到这里了,当然也没有说尽装饰器的妙用,比如:装饰类...其原理是用类来当做装饰器,类里面需要用到__call__方法,至于装饰类的用法感兴趣的朋友自行百度咯!

 


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn