Lassen Sie uns einen Python "Dekorator" erstellen. Erstellen Sie ein generisches Dekorations-Framework.
Zu machen. Zuerst werden wir das Dekorations-Framework (Ziel) vorstellen und darauf hinarbeiten.
Dieser Artikel wurde geschrieben, um vorgelesen zu werden (in Richtung des Ziels durch Wiederholen von Versuch und Irrtum) und ist ein langer Satz, daher werde ich nur beschreiben, wie der Dekorateur (Framework, Framework) zuerst geschrieben wird. [Vollversion ist zuletzt aufgeführt](# decorator_frameworkpy-full-version) einschließlich des Tests. Wenn Sie beschäftigt sind, schauen Sie es sich bitte an, wenn Sie Zeit haben.
decorator_framework.py
from functools import wraps
def my_decorator( *args, **kwargs ):
#Für Dekorateure, die eindeutig keine Argumente vertreten, beginnen Sie hier
# _my_Es ist auch möglich, den Namen des Dekorateurs zu ändern und global zu definieren
def _my_decorator( func ):
# _my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
print( "_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier" )
@wraps(func)
def _my_decorator_body( *body_args, **body_kwargs ):
#Die Vorverarbeitung erfolgt hier
print( "Die Vorverarbeitung erfolgt hier", args, kwargs, body_args, body_kwargs )
try:
#Ausführung des dekorierten Körpers
ret = func( *body_args, **body_kwargs )
except:
raise
#Die Nachbearbeitung erfolgt hier
print( "Die Nachbearbeitung erfolgt hier", args, kwargs, body_args, body_kwargs )
return ret
#Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
print( "Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2" )
return _my_decorator_body
#Dies ist das Ende des Dekorateurs, das eindeutig kein Argument annimmt
#Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
print( "Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1" )
if len(args) == 1 and callable( args[0] ):
#Wenn der Dekorateur ohne Argumente aufgerufen wird, behandeln Sie dies hier
print( "No arguments" )
return _my_decorator( args[0] )
else:
#Wenn der Dekorateur mit einem Argument aufgerufen wird, verarbeiten Sie es hier
print( "There are some arguments:", args )
return _my_decorator
Noch einfacher.
decorator_framework.py Einfache Version
from functools import wraps
def my_decorator( *args, **kwargs ):
def _my_decorator( func ):
# _my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
@wraps(func)
def _my_decorator_body( *body_args, **body_kwargs ):
#Die Vorverarbeitung erfolgt hier
try:
#Ausführung des dekorierten Körpers
ret = func( *body_args, **body_kwargs )
except:
raise
#Die Nachbearbeitung erfolgt hier
return ret
#Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
return _my_decorator_body
#Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
if len(args) == 1 and callable( args[0] ):
#Wenn der Dekorateur ohne Argumente aufgerufen wird, behandeln Sie dies hier
return _my_decorator( args[0] )
else:
#Wenn der Dekorateur mit einem Argument aufgerufen wird, verarbeiten Sie es hier
return _my_decorator
Beginnen wir nun mit der Erstellung eines Dekorateurs.
Das Glossar beschreibt den Dekorateur wie folgt:
decorator dt> (decorator) Eine Funktion, die eine andere Funktion zurückgibt, normalerweise
@wrapper span> code> angewendet. Ein häufiger Anwendungsfall für Dekorateure ist
< span class = "pre"> classmethod () span> code>
undstaticmethod () span> code>
. p>Die Grammatik des Dekorateurs ist Syntaxzucker. Die folgenden zwei Funktionsdefinitionen sind semantisch identisch: p>
def f(...): ... f = staticmethod(f)@staticmethod def f(...): ...
Das gleiche Konzept existiert in Klassen, wird jedoch selten verwendet. Weitere Informationen zu Dekorateuren finden Sie unter Funktionsdefinition span> und
Klassendefinition span> . p>
(Zitiert aus Dekorator-Glossar - Python 3.8.1-Dokumentation)
In dem oben zitierten Beispiel ist "@ staticmethod" der Dekorateur.
Darüber hinaus lautet die Beschreibung des Funktionsdefinitionselements wie folgt.
Eine oder mehrere Funktionsdefinitionen decorator span> umschließen. Wenn Sie eine Funktion definieren, wird der Dekoratorausdruck in dem Bereich ausgewertet, der die Funktionsdefinition enthält. Das Ergebnis muss ein aufrufbares Objekt sein, dessen einziges Argument ein Funktionsobjekt ist. Anstelle eines Funktionsobjekts ist der zurückgegebene Wert an den Funktionsnamen gebunden. Mehrere Dekorateure werden verschachtelt und angewendet. Zum Beispiel der folgende Code: p>
@f1(arg) @f2 def func(): passentspricht in etwa p>
def func(): pass func = f1(arg)(f2(func))Im vorherigen Code ist es jedoch nicht möglich, die ursprüngliche Funktion vorübergehend an den Namen
func span> code> zu binden. Außer wo es so etwas nicht gibt. p>
(Zitiert aus Funktionsdefinition - 8 zusammengesetzte Anweisung - Python 3.8.1-Dokumentation)
Die Grammatik des Dekorateurs ist Syntaxzucker
Einige Leute kennen das Wort "Synthaxzucker" möglicherweise nicht. Einfach ausgedrückt, eine komplexe (verschleierte) Beschreibung, die durch eine einfache Notation ersetzt werden kann oder umgekehrt, wird als "Syntaxzucker" bezeichnet.
Mit anderen Worten, der Dekorator ist ein wiederbeschreibbarer Ersatz, ähnlich dem, was andere Sprachen als "Makro" bezeichnen. (Die Ersetzungsmethode wurde jedoch festgelegt.)
Betrachten Sie das Beispiel im Glossar.
Beispiel 1
def f(...):
...
f = staticmethod(f)
Mit diesem Beispiel 1
Beispiel 2
@staticmethod
def f(...):
...
Es heißt, dass dieses Beispiel 2 dasselbe ist. Was meinst du.
Was Beispiel 1 tut, ist
staticmethod ()
mit der definierten Funktion f ()
(Funktionsobjekt) als Argument aus.staticmethod ()
erneut in die Funktion f.
Es bedeutet das.Beispiel 2 ist eine vereinfachte Version davon, und "@ staticmethod" wird als "Dekorateur" bezeichnet.
Wenn danach die Funktion "f ()" tatsächlich aufgerufen wird, wird die neu definierte Funktion als Rückgabewert von "staticmethod ()" aufgerufen.
Da @ staticmethod
tatsächlich ein standarddefinierter Dekorator ist, ist es als Beispiel nicht praktisch, daher werden wir unten als @ my_decorator
fortfahren.
Allein anhand des obigen Beispiels können Sie einige Dinge erkennen.
my_decorator
ist eine Funktion. (Um genau zu sein, "aufrufbares Objekt")my_decorator ()
nimmt ein Funktionsobjekt als Argument.my_decorator ()
gibt eine Funktion zurück.darüber.
Der folgende Code zeigt, was wir oben gefunden haben.
my_decorator Erster Schritt
def my_decorator(func):
return func
Es tut nichts und gibt die als Argument empfangene Funktion zurück. Aufrufe, die kein @ verwenden, sehen aus wie Beispiel 001.
sample 001
>>> def my_decorator_001(func):
... return func
...
>>> def base_func():
... print( "in base_func()" )
...
>>> base_func = my_decorator_001( base_func )
>>> base_func()
in base_func()
>>>
Wenn Sie "@" als Dekorateur verwenden, sieht es wie Beispiel 002 aus.
sample 002
>>> def my_decorator_001(func):
... return func
...
>>> @my_decorator_001
... def base_func():
... print( "in base_func()" )
...
>>> base_func()
in base_func()
>>>
Sie wissen nicht, was damit los ist. Es ist jedoch bemerkenswert, dass bei der Eingabe von "@ my_decorator_001" auf eine kontinuierliche Eingabe gewartet wird.
Fügen wir eine Anzeige in "my_decorator ()" hinzu.
sample 003
>>> def my_decorator_002(func):
... print( "in my_decorator_002()" )
... return func
...
>>> def base_func():
... print( "in base_func()" )
...
>>> base_func = my_decorator_002(base_func)
in my_decorator_002()
>>> base_func()
in base_func()
>>>
Als nächstes verwenden wir einen Dekorateur.
sample 004
>>> def my_decorator_002(func):
... print( "in my_decorator_002()" )
... return func
...
>>> @my_decorator_002
... def base_func():
... print( "in base_func()" )
...
in my_decorator_002()
>>> base_func()
in base_func()
>>>
Die Funktion "my_decorator_002 ()" wird nicht ausgeführt, wenn "@ my_decorator_002" geschrieben wird, und "in my_decorator ()" wird angezeigt, wenn die Definition von "def base_func ():" darunter abgeschlossen ist. Der zu beachtende Punkt ist, dass dies dasselbe ist, was passiert, wenn Sie in Beispiel 003 base_func = my_decorator_002 (base_func)
ausführen.
Zuvor wurde die Funktion, die dem Argument des Dekorateurs zugewiesen wurde, wie bei return zurückgegeben. Was ist die Rückgabefunktion?
Versuchen wir zunächst, eine globale Funktion zu verwenden, um zu sehen, was passiert, wenn wir eine andere Funktion zurückgeben.
sample 005
>>> def global_func():
... print( "in global_func()" )
...
>>> def my_decorator_005(func):
... print( "in my_decorator_005()" )
... return global_func
...
>>> @my_decorator_005
... def base_func():
... print( "in base_func()" )
...
in my_decorator_005()
>>> base_func()
in global_func()
>>>
Durch Ausführen von "base_func ()" wurde "global_func ()" ohne Probleme aufgerufen.
Aber natürlich läuft das ursprüngliche base_func ()
nicht.
Erwägen Sie, die ursprüngliche Funktion auszuführen, während Sie die neue Funktion ausführen.
Die ursprüngliche Funktion wird als Argument der Decorator-Funktion übergeben. Wenn Sie sie also aufrufen, wird die ursprüngliche Funktion ausgeführt.
sample 006
>>> def global_func():
... print( "in global_func()" )
...
>>> def my_decorator_006(func):
... print( "in my_decorator_006()" )
... func() #Rufen Sie die ursprüngliche Funktion auf
... return global_func
...
>>> @my_decorator_006
... def base_func():
... print( "in base_func()" )
...
in my_decorator_006()
in base_func() #Ich habe hier die ursprüngliche Funktion aufgerufen
>>> base_func() #* Ich möchte hier die ursprüngliche Funktion aufrufen
in global_func()
>>>
Die ursprüngliche Funktion "base_func ()" wurde aufgerufen, als "@ my_decorator_006" angegeben wurde. Das Timing, wenn Sie "base_func ()" aufrufen möchten, ist, wenn Sie "base_func ()" mit "*" aufrufen. Mit anderen Worten, wir wollen "base_func ()" innerhalb von "global_func ()" aufrufen.
Lassen Sie es uns ein wenig ändern, damit es in global_func ()
aufgerufen werden kann.
sample 007
>>> #Behalten Sie die ursprüngliche Funktion bei
... original_func = None
>>>
>>> def global_func():
... global original_func
... print( "in global_func()" )
... original_func() #Die ursprüngliche Funktion sollte aufgerufen werden
...
>>> def my_decorator_007(func):
... global original_func
... print( "in my_decorator_007()" )
... original_func = func #Die an das Argument übergebene Funktion ist ein globales Original_Zu func
... return global_func
...
>>> @my_decorator_007
... def base_func():
... print( "in base_func()" )
...
in my_decorator_007()
>>> base_func() #* Ich möchte hier die ursprüngliche Funktion aufrufen
in global_func()
in base_func()
>>>
Es ist ein wenig verwirrend, aber ich konnte es von "global_func ()" aus aufrufen, indem ich die globale Variable "original_func" vorbereitete und die hier als Argument übergebene "func" speicherte.
Es gibt jedoch ein Problem damit. Wenn ich versuche, den Dekorator für mehrere Funktionen zu verwenden, funktioniert er nicht wie erwartet.
sample 008
>>> #Behalten Sie die ursprüngliche Funktion bei
... original_func = None
>>>
>>> def global_func():
... global original_func
... print( "in global_func()" )
... original_func() #Die ursprüngliche Funktion sollte aufgerufen werden
...
>>> def my_decorator_007(func):
... global original_func
... print( "in my_decorator_007()" )
... original_func = func #Die an das Argument übergebene Funktion ist ein globales Original_Zu func
... return global_func
...
>>> @my_decorator_007
... def base_func():
... print( "in base_func()" )
...
in my_decorator_007()
>>> @my_decorator_007
... def base_func_2():
... print( "in base_func_2()" )
...
in my_decorator_007()
>>> base_func() # "in base_func()"Ich möchte, dass du angezeigt wirst
in global_func()
in base_func_2() ← "in base_func()"nicht"in base_func_2()"Wurde angezeigt
>>> base_func_2() # "in base_func_2()"Ich möchte, dass du angezeigt wirst
in global_func()
in base_func_2()
>>>
Der Aufruf von "base_func ()" hat "base_func_2 ()" ausgeführt. Da es nur eine globale Variable "global_func" gibt, wird die zuletzt zugewiesene "base_func_2" ausgeführt.
Lassen Sie uns über "Schließen" sprechen, damit der Dekorateur mehrmals verwendet werden kann.
Schauen Sie sich sample_009.py
so an:
sample_009.py
1 def outer(outer_arg):
2 def inner(inner_arg):
3 # outer_arg und inner_Ausgabe arg
4 print( "outer_arg: " + outer_arg + ", inner_arg: " + inner_arg )
5 return inner # inner()Gibt ein Funktionsobjekt zurück
6
7 f1 = outer("first")
8 f2 = outer("second")
9
10 f1("f1")
11 f2("f2")
Führen Sie nach "f1 = äußere (" erste ")" in Zeile 7 "f2 = äußere (" zweite ")" in Zeile 8 aus. Jedem wird ein "inneres" Funktionsobjekt zugewiesen, aber was ist mit "f1 (" f1 ")" in Zeile 10 und "f2 (" f2 ")" in Zeile 11?
In der 8. Zeile steht "f2 = Outer (" second ")", in der 4. Zeile "print ()" wird "Outer_arg" zu "Second".
Die Ausgabe von "f1 (" f1 ")" in Zeile 10 und "f2 (" f2 ")" in Zeile 11 sind jeweils.
outer_arg: second, inner_arg: f1
outer_arg: second, inner_arg: f2
Es scheint so als ...
Wenn ich es starte, sieht es so aus:
sample 009
>>> def outer(outer_arg):
... def inner(inner_arg):
... # outer_arg und inner_Ausgabe arg
... print( "outer_arg:", outer_arg, ", inner_arg:", inner_arg )
... return inner # inner()Gibt ein Funktionsobjekt zurück
...
>>> f1 = outer("first")
>>> f2 = outer("second")
>>>
>>> f1("f1")
outer_arg: first, inner_arg: f1
>>> f2("f2")
outer_arg: second, inner_arg: f2
>>>
das ist,
def inner (): ...
inner ()
wird jedes Mal "ausgeführt", wenn die Funktionäußere ()
aufgerufen wird.Verwenden Sie diese Option, damit der Dekorateur mehrmals verwendet werden kann.
In Beispiel 009 haben wir eine globale Funktion verwendet, aber um die Funktion des Verschlusses zu verwenden, definieren wir die Funktion innerhalb der Dekorationsfunktion.
sample_010.py
def my_decorator_010(func):
print( "in my_decorator_010()" )
def inner():
print( "in inner() and calling", func )
func()
print( "in inner() and returnd from ", func )
print( "in my_decorator_010(), leaving ..." )
return inner
@my_decorator_010
def base_func():
print( "in base_func()" )
@my_decorator_010
def base_func_2():
print( "in base_func_2()" )
base_func() # "in base_func()"Ich möchte, dass du angezeigt wirst
base_func_2() # "in base_func_2()"Ich möchte, dass du angezeigt wirst
Lassen Sie uns dies tun (interaktiv eingeben).
sample 010
>>> def my_decorator_010(func):
... print( "in my_decorator_010()" )
... def inner():
... print( "in inner() and calling", func )
... func()
... print( "in inner() and returnd from ", func )
... print( "in my_decorator_010(), leaving ..." )
... return inner
...
>>> @my_decorator_010
... def base_func():
... print( "in base_func()" )
...
in my_decorator_010()
in my_decorator_010(), leaving ...
>>> @my_decorator_010
... def base_func_2():
... print( "in base_func_2()" )
...
in my_decorator_010()
in my_decorator_010(), leaving ...
>>> base_func() # "in base_func()"Ich möchte, dass du angezeigt wirst
in inner() and calling <function base_func at 0x769d1858>
in base_func()
in inner() and returnd from <function base_func at 0x769d1858>
>>> base_func_2() # "in base_func_2()"Ich möchte, dass du angezeigt wirst
in inner() and calling <function base_func_2 at 0x769d1930>
in base_func_2()
in inner() and returnd from <function base_func_2 at 0x769d1930>
>>>
Ich habe das erwartete Ergebnis erhalten.
Nachdem wir das Obige organisiert hatten, stellten wir fest, dass der Dekorateur wie folgt codiert werden sollte.
sample 011
def my_decorator_011(func):
def inner():
#Schreiben Sie hier den Pre-Call-Prozess
func()
#Schreiben Sie hier die Nachbearbeitung
return inner
Die Dekorateure sind bisher
Wenn Sie einen Allzweckdekorateur herstellen möchten, müssen Sie die beiden oben genannten Bedingungen erfüllen.
Das Programm, das den Rückgabewert berücksichtigt, ist wie folgt.
sample_012.py
def my_decorator_012(func):
def inner():
#Schreiben Sie hier den Pre-Call-Prozess
ret = func()
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
@my_decorator_012
def base_func_1():
print( "in base_func_1()" )
return 1
@my_decorator_012
def base_func_2():
print( "in base_func_2()" )
r1 = base_func_1()
print( r1 )
base_func_2()
Das Ausführungsergebnis ist wie folgt.
sample 012
>>> def my_decorator_012(func):
... def inner():
... #Schreiben Sie hier den Pre-Call-Prozess
... ret = func()
... #Schreiben Sie hier die Nachbearbeitung
... return ret
... return inner
...
>>> @my_decorator_012
... def base_func_1():
... print( "in base_func_1()" )
... return 1
...
>>> @my_decorator_012
... def base_func_2():
... print( "in base_func_2()" )
...
>>> r1 = base_func_1()
in base_func_1()
>>> print( r1 )
1
>>> base_func_2()
in base_func_2()
>>>
Es funktionierte auch, wenn es keinen Rückgabewert gab (base_func_2 ()
).
Siehe auch: Parameter-Glossar - Python 3.8.1-Dokumentation
Funktionsargumente können als formale Argumente empfangen werden, für die die Anzahl der Argumente und die Schlüsselwortspezifikation nicht durch den Positionsparameter variabler Länge und den Schlüsselwortparameter variabler Länge bestimmt werden.
Position variabler Länge em>: Beliebig viele Positionsargumente (zusätzlich zu Positionsargumenten, die bereits von anderen formalen Argumenten empfangen wurden) Angegeben. Solche formalen Argumente sollten vor dem formalen Argumentnamen stehen, z. B. args em> unten,
* span> < Sie kann durch Hinzufügen von / code>: p> definiert werden
def func(*args, **kwargs): ...Schlüsselwörter mit variabler Länge em>: Bei einer beliebigen Anzahl von Schlüsselwortargumenten (zusätzlich zu Schlüsselwortargumenten, die bereits von anderen formalen Argumenten empfangen wurden) Angegeben. Solche formalen Argumente sollten vor dem formalen Argumentnamen stehen, z. B. kwargs em> im obigen Beispiel,
** strong> Sie kann durch Hinzufügen von span> code> definiert werden. p> li>
(Zitiert aus Parameter-Glossar - Python 3.8.1-Dokumentation)
Einfach ausgedrückt, können Sie ein variables Argument verwenden, indem Sie "(* args, ** kwargs)" als Funktionsargument angeben.
Auf diese Weise kann der Dekorator für eine Funktion, die ein Argument akzeptiert, wie folgt geschrieben werden:
sample_013.py
def my_decorator_013(func):
def inner( *args, **kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
ret = func( *args, **kwargs )
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
@my_decorator_013
def base_func_1(arg1, arg2, arg3="arg3"):
print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
return 1
@my_decorator_013
def base_func_2():
print( "in base_func_2()" )
r1 = base_func_1("arg1","arg2")
print( r1 )
base_func_2()
Das Folgende ist das Ausführungsergebnis.
sample 013
>>> def my_decorator_013(func):
... def inner( *args, **kwargs ):
... #Schreiben Sie hier den Pre-Call-Prozess
... ret = func( *args, **kwargs )
... #Schreiben Sie hier die Nachbearbeitung
... return ret
... return inner
...
>>> @my_decorator_013
... def base_func_1(arg1, arg2, arg3="arg3"):
... print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
... return 1
...
>>> @my_decorator_013
... def base_func_2():
... print( "in base_func_2()" )
...
>>> r1 = base_func_1("arg1","arg2")
in base_func_1(arg1, arg2, arg3)
>>> print( r1 )
1
>>> base_func_2()
in base_func_2()
>>>
Berücksichtigen Sie auch Ausnahmen.
sample_014.py
def my_decorator_014(func):
def inner( *args, **kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
try:
ret = func( *args, **kwargs )
except:
raise
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
@my_decorator_014
def base_func_1(arg1, arg2, arg3="arg3"):
print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
return 1
@my_decorator_014
def base_func_2():
print( "in base_func_2()" )
na = 1 / 0 #Eine Nullteilungsausnahme tritt auf
r1 = base_func_1("arg1","arg2")
print( r1 )
try:
base_func_2()
except ZeroDivisionError:
print( "Zero Division Error" )
Das Folgende ist das Ausführungsergebnis.
sample 014
>>> def my_decorator_014(func):
... def inner( *args, **kwargs ):
... #Schreiben Sie hier den Pre-Call-Prozess
... try:
... ret = func( *args, **kwargs )
... except:
... raise
... #Schreiben Sie hier die Nachbearbeitung
... return ret
... return inner
...
>>> @my_decorator_014
... def base_func_1(arg1, arg2, arg3="arg3"):
... print( "in base_func_1({}, {}, {})".format(arg1, arg2, arg3 ) )
... return 1
...
>>> @my_decorator_014
... def base_func_2():
... print( "in base_func_2()" )
... na = 1 / 0 #Eine Nullteilungsausnahme tritt auf
...
>>> r1 = base_func_1("arg1","arg2")
in base_func_1(arg1, arg2, arg3)
>>> print( r1 )
1
>>>
>>> try:
... base_func_2()
... except ZeroDivisionError:
... print( "Zero Division Error" )
...
in base_func_2()
Zero Division Error
>>>
Da ein Dekorateur eine Funktion (ein aufrufbares Objekt) ist, sollten Sie ihm ein Argument übergeben.
Die am Anfang zitierte Beschreibung der Funktionsdefinition enthielt auch ein Beispiel eines Dekorateurs mit Argumenten.
@f1(arg)
@f2
def func(): pass
def func(): pass
func = f1(arg)(f2(func))
Da die Dekoratoren verschachtelt sind, sollten Sie der Einfachheit halber nur einen Dekorateur berücksichtigen.
@my_decorator('Streit')
def func(arg1, arg2, arg3):
pass
Dies entspricht unten.
def func(arg1, arg2, arg3):
pass
func = my_decorator('Streit')(func)
Es bedeutet, "_my_decorator (func)" mit der Funktion (angenommen _my_decorator) auszuführen, die durch Aufrufen von "my_decorator (" argument ")" zurückgegeben wird.
Die Verschachtelung wird einen Schritt tiefer, und die äußerste Funktion (Dekorator) erhält das Argument, und der vorhandene Dekorator ist darin enthalten.
sample015.py
def my_decorator_015( arg1 ):
def _my_decorator( func ):
def inner( *args, **kwargs ):
print( "in inner, arg1={}, func={}".format(arg1, func.__name__) )
ret = func( *args, **kwargs )
print( "in inner leaving ..." )
return ret
return inner
return _my_decorator
Das Folgende ist das Ausführungsergebnis.
sample 015
>>> def my_decorator_015( arg1 ):
... def _my_decorator( func ):
... def inner( *args, **kwargs ):
... print( "in inner, arg1={}, func={}".format(arg1, func.__name__) )
... ret = func( *args, **kwargs )
... print( "in inner leaving ..." )
... return ret
... return inner
... return _my_decorator
...
>>> @my_decorator_015('Streit')
... def f_015( arg1, arg2, arg3 ):
... print( "in func( {}, {}, {} )".format(arg1, arg2, arg3) )
...
>>> f_015( "Argument 1", "Argument 2", "Argument 3" )
in inner, arg1=Streit, func=f_015
in func(Argument 1,Argument 2,Argument 3)
in inner leaving ...
>>>
Wenn Sie den Dekorator mit Argumenten aufrufen, werden der untere Teil arg1 und func in _my_decorator_body gespeichert.
print( "in _my_decorator_body, arg1={}, func={}".format(arg1, func.__name__) )
Wenn Sie also f_015 () aufrufen, werden die gespeicherten Argumente arg1 und func referenziert.
Der Dekorateur, der kein Argument annimmt, ist wie folgt.
sample_014.py
def my_decorator_014(func):
def inner( *args, **kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
try:
ret = func( *args, **kwargs )
except:
raise
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
Der Dekorateur, der das Argument übergibt, ist wie folgt.
sample_016.py
def my_decorator_016( arg1 ):
def _my_decorator( func ):
def inner( *args, **kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
try:
ret = func( *args, **kwargs )
except:
raise
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
return _my_decorator
Was passiert, wenn Sie einen Dekorateur verwenden, der Argumente annimmt, ohne Argumente anzugeben?
sample_017.py
def my_decorator_017( arg1 ):
def _my_decorator( func ):
def inner( *args, **kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
try:
ret = func( *args, **kwargs )
except:
raise
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
return _my_decorator
@my_decorator_017
def f_017( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_017.__name__, arg1, arg2, arg3) )
f_017( "Argument 1", "Argument 2", "Argument 3" )
Ausführungsergebnis
$ python sample_017.py
Traceback (most recent call last):
File "sample_017.py", line 21, in <module>
f_017( "Argument 1", "Argument 2", "Argument 3" )
TypeError: _my_decorator() takes 1 positional argument but 3 were given
$
Dies liegt daran, dass wir keine Argumente an "@ my_decorator_017" übergeben haben, daher heißt es "f_017 = my_decorator_017 (f_017)". Es gibt zwei Möglichkeiten, dies zu vermeiden.
()
hinzufügen und es als Argument variabler Länge erhalten.Dies ist das erste Beispiel.
sample_018.py
def my_decorator_018( *args, **kwargs ):
def _my_decorator( func ):
def inner( *inner_args, **inner_kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
try:
ret = func( *inner_args, **inner_kwargs )
except:
raise
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
return _my_decorator
@my_decorator_018()
def f_018( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_018.__name__, arg1, arg2, arg3) )
f_018( "Argument 1", "Argument 2", "Argument 3" )
Ausführungsergebnis
$ python sample_018.py
in inner(Argument 1,Argument 2,Argument 3)
Die zweite Methode verwendet das erste Argument des Dekorateurs zur Beurteilung.
sample_019.py
def my_decorator_019( *args, **kwargs ):
def _my_decorator( func ):
def inner( *inner_args, **inner_kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
try:
ret = func( *inner_args, **inner_kwargs )
except:
raise
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
if len(args) == 1 and callable(args[0]):
return _my_decorator( args[0] )
else:
return _my_decorator
@my_decorator_019
def f_019_1( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_019_1.__name__, arg1, arg2, arg3) )
@my_decorator_019()
def f_019_2( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_019_2.__name__, arg1, arg2, arg3) )
@my_decorator_019('arg')
def f_019_3( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_019_3.__name__, arg1, arg2, arg3) )
f_019_1( "Argument 1", "Argument 2", "Argument 3" )
f_019_2( "Argument 1", "Argument 2", "Argument 3" )
f_019_3( "Argument 1", "Argument 2", "Argument 3" )
Um zwischen den an den Dekorateur übergebenen Argumenten und den beim Aufruf von "f_019_ * ()" übergebenen Argumenten zu unterscheiden, werden sie durch "(* args, ** kwargs)" bzw. "(* inner_args, ** inner_kwargs)" unterschieden. tun.
Ausführungsergebnis
$ python sample_019.py
in inner(Argument 1,Argument 2,Argument 3)
in inner(Argument 1,Argument 2,Argument 3)
in inner(Argument 1,Argument 2,Argument 3)
Jetzt, wo wir ein universelles Dekorations-Framework haben ... würde ich gerne denken ... Alle aufgerufenen Funktionsnamen (f_019_ * .__ name__
) sind jetzt inner
.
Sie können sehen, dass die Funktion "inner" anstelle der ursprünglichen Funktion aufgerufen wird.
Die ursprüngliche Funktion kann sich jedoch auf Attribute wie Name (__name__
) und Dokumentation ( __doc__
) beziehen.
Es gibt einen Dekorateur namens "@ wraps", der dies vermeiden kann.
Siehe auch: [@ functools.wraps () --functools --- Manipulation von Funktionen höherer Ordnung und aufrufbaren Objekten - Python 3.8.1-Dokumentation](https://docs.python.org/ja/3/library/functools.html? Highlight = Wraps # functools.wraps) Siehe auch: [functools.update_wrapper () --functools --- Manipulieren von Funktionen höherer Ordnung und aufrufbaren Objekten - Python 3.8.1-Dokumentation](https://docs.python.org/ja/3/library/functools.html?highlight = wraps # functools.update_wrapper)
Wenn Sie dies vor dem "def inner" dekorieren, können Sie die Attribute des an die äußere Funktion ("_my_decorator ()") übergebenen Arguments "func" umbrechen und beibehalten.
sample_020.py
from functools import wraps
def my_decorator_020( *args, **kwargs ):
def _my_decorator( func ):
@wraps(func)
def inner( *inner_args, **inner_kwargs ):
#Schreiben Sie hier den Pre-Call-Prozess
try:
ret = func( *inner_args, **inner_kwargs )
except:
raise
#Schreiben Sie hier die Nachbearbeitung
return ret
return inner
if len(args) == 1 and callable(args[0]):
return _my_decorator( args[0] )
else:
return _my_decorator
@my_decorator_020
def f_020_1( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_020_1.__name__, arg1, arg2, arg3) )
@my_decorator_020()
def f_020_2( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_020_2.__name__, arg1, arg2, arg3) )
@my_decorator_020('arg')
def f_020_3( arg1, arg2, arg3 ):
print( "in {}( {}, {}, {} )".format(f_020_3.__name__, arg1, arg2, arg3) )
f_020_1( "Argument 1", "Argument 2", "Argument 3" )
f_020_2( "Argument 1", "Argument 2", "Argument 3" )
f_020_3( "Argument 1", "Argument 2", "Argument 3" )
Für "sample_019.py" wird "@wraps (func)" vor "from functools import wraps" und "def inner ()" hinzugefügt.
func
ist die Funktion, die als Argument an _my_decorator ()
übergeben wird.
Ausführungsergebnis
$ python sample_020.py
in f_020_1(Argument 1,Argument 2,Argument 3)
in f_020_2(Argument 1,Argument 2,Argument 3)
in f_020_3(Argument 1,Argument 2,Argument 3)
$
Der Name der Funktion (f_019_ * .__ name__
) wird als Name der ursprünglichen Funktion anstelle von inner
beibehalten.
Es war ein langer Weg, aber schließlich ist das Dekorations-Framework vollständig.
Da es generisch erstellt wurde, ist es "def my_decorator (* args, ** kwargs):", aber wenn die zu empfangenden Argumente festgelegt sind, ist es besser zu klären.
decorator_framework.py Full Version
decorator_framework.py Full Version
#!/usr/bin/env python
# -*- coding: utf-8 -*-
###########################################
#Dekorateur(decorator)
###########################################
from functools import wraps
def my_decorator( *args, **kwargs ):
"""
for doctest
>>> @my_decorator
... def f1( arg1 ):
... print( arg1 )
...
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
No arguments
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
>>> @my_decorator('mytest1')
... def f2( arg2 ):
... print( arg2 )
...
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
There are some arguments: ('mytest1',)
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
>>> @my_decorator
... def f3( arg1 ):
... print( arg1 )
... a = 1/0
...
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
No arguments
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
>>> @my_decorator('mytest2')
... def f4( arg2 ):
... print( arg2 )
... a = 1/0
...
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
There are some arguments: ('mytest2',)
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
>>> try:
... f1( "Hello, World! #1" )
... except:
... print( "error #1" )
...
Die Vorverarbeitung erfolgt hier(<function f1 at 0x...>,) {} ('Hello, World! #1',) {}
Hello, World! #1
Die Nachbearbeitung erfolgt hier... {} ('Hello, World! #1',) {}
>>> try:
... f2( "Hello, World! #2" )
... except:
... print( "error #2" )
...
Die Vorverarbeitung erfolgt hier('mytest1',) {} ('Hello, World! #2',) {}
Hello, World! #2
Die Nachbearbeitung erfolgt hier('mytest1',) {} ('Hello, World! #2',) {}
>>> try:
... f3( "Hello, World! #3" )
... except:
... print( "error #3" )
...
Die Vorverarbeitung erfolgt hier(<function f3 at 0x...>,) {} ('Hello, World! #3',) {}
Hello, World! #3
error #3
>>> try:
... f4( "Hello, World! #4" )
... except:
... print( "error #4" )
...
Die Vorverarbeitung erfolgt hier('mytest2',) {} ('Hello, World! #4',) {}
Hello, World! #4
error #4
>>>
"""
#Klicken Sie hier für Dekorateure, die eindeutig keine Argumente annehmen
# _my_Dekorator umbenennen und global definieren
def _my_decorator( func ):
# _my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
print( "_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier" )
@wraps(func)
def _my_decorator_body( *body_args, **body_kwargs ):
#Die Vorverarbeitung erfolgt hier
print( "Die Vorverarbeitung erfolgt hier", args, kwargs, body_args, body_kwargs )
try:
#Ausführung des dekorierten Körpers
ret = func( *body_args, **body_kwargs )
except:
raise
#Die Nachbearbeitung erfolgt hier
print( "Die Nachbearbeitung erfolgt hier", args, kwargs, body_args, body_kwargs )
return ret
#Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
print( "Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2" )
return _my_decorator_body
#Dies ist das Ende des Dekorateurs, das eindeutig kein Argument annimmt
#Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
print( "Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1" )
if len(args) == 1 and callable( args[0] ):
#Wenn der Dekorateur ohne Argumente aufgerufen wird, behandeln Sie dies hier
print( "No arguments" )
return _my_decorator( args[0] )
else:
#Wenn der Dekorateur mit einem Argument aufgerufen wird, verarbeiten Sie es hier
print( "There are some arguments:", args )
return _my_decorator
###########################################
# unitttest
###########################################
import unittest
from io import StringIO
import sys
class Test_My_Decorator(unittest.TestCase):
def setUp(self):
self.saved_stdout = sys.stdout
self.stdout = StringIO()
sys.stdout = self.stdout
def tearDown(self):
sys.stdout = self.saved_stdout
def test_decorator_noarg(self):
@my_decorator
def t1(arg0):
print( arg0 )
t1("test_decorator_noarg")
import re
s = re.sub('0x[0-9a-f]+', '0x', self.stdout.getvalue())
self.assertEqual(s,
"Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1\n" +
"No arguments\n" +
"_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier\n" +
"Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2\n" +
"Die Vorverarbeitung erfolgt hier(<function Test_My_Decorator.test_decorator_noarg.<locals>.t1 at 0x>,) {} ('test_decorator_noarg',) {}\n" +
"test_decorator_noarg\n" +
"Die Nachbearbeitung erfolgt hier(<function Test_My_Decorator.test_decorator_noarg.<locals>.t1 at 0x>,) {} ('test_decorator_noarg',) {}\n"
)
def test_decorator_witharg(self):
@my_decorator('with arg')
def t1(arg0):
print( arg0 )
t1("test_decorator_witharg")
self.assertEqual(self.stdout.getvalue(),
"Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1\n" +
"There are some arguments: ('with arg',)\n" +
"_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier\n" +
"Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2\n" +
"Die Vorverarbeitung erfolgt hier('with arg',) {} ('test_decorator_witharg',) {}\n" +
"test_decorator_witharg\n" +
"Die Nachbearbeitung erfolgt hier('with arg',) {} ('test_decorator_witharg',) {}\n"
)
def test_functionname(self):
@my_decorator
def t1():
return t1.__name__
f_name = t1()
self.assertEqual( f_name, "t1" )
def test_docattribute(self):
@my_decorator
def t1():
"""Test Document"""
pass
self.assertEqual( t1.__doc__, "Test Document" )
###########################################
# main
###########################################
if __name__ == '__main__':
@my_decorator
def f1( arg1 ):
print( arg1 )
@my_decorator('mytest1')
def f2( arg2 ):
print( arg2 )
@my_decorator
def f3( arg1 ):
print( arg1 )
a = 1/0
@my_decorator('mytest2')
def f4( arg2 ):
print( arg2 )
a = 1/0
try:
f1( "Hello, World! #1" )
except:
print( "error #1" )
try:
f2( "Hello, World! #2" )
except:
print( "error #2" )
try:
f3( "Hello, World! #3" )
except:
print( "error #3" )
try:
f4( "Hello, World! #4" )
except:
print( "error #4" )
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS)
unittest.main()
Beispielausführung
$ python decorator_framework.py
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
No arguments
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
There are some arguments: ('mytest1',)
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
No arguments
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#1
There are some arguments: ('mytest2',)
_my_decorator_body()Wenn vor dem Definieren eine Verarbeitung erforderlich ist, schreiben Sie diese hier
Wenn bei der Beschreibung des Dekorateurs eine Verarbeitung erforderlich ist, schreiben Sie diese hier#2
Die Vorverarbeitung erfolgt hier(<function f1 at 0x76973a08>,) {} ('Hello, World! #1',) {}
Hello, World! #1
Die Nachbearbeitung erfolgt hier(<function f1 at 0x76973a08>,) {} ('Hello, World! #1',) {}
Die Vorverarbeitung erfolgt hier('mytest1',) {} ('Hello, World! #2',) {}
Hello, World! #2
Die Nachbearbeitung erfolgt hier('mytest1',) {} ('Hello, World! #2',) {}
Die Vorverarbeitung erfolgt hier(<function f3 at 0x7685bd20>,) {} ('Hello, World! #3',) {}
Hello, World! #3
error #3
Die Vorverarbeitung erfolgt hier('mytest2',) {} ('Hello, World! #4',) {}
Hello, World! #4
error #4
....
----------------------------------------------------------------------
Ran 4 tests in 0.005s
OK
$
unittest
$ python -m unittest -v decorator_framework.py
test_decorator_noarg (decorator_framework.Test_My_Decorator) ... ok
test_decorator_witharg (decorator_framework.Test_My_Decorator) ... ok
test_docattribute (decorator_framework.Test_My_Decorator) ... ok
test_functionname (decorator_framework.Test_My_Decorator) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.006s
OK
$
Recommended Posts