Einführung
In diesem Lab lernen Sie etwas über Decorators (Dekorateure) in Python kennen, eine leistungsstarke Funktion, die das Verhalten von Funktionen und Methoden ändern kann. Decorators werden üblicherweise für Aufgaben wie Logging (Protokollierung), Leistungsmessung, Zugriffskontrolle und Typüberprüfung verwendet.
Sie werden lernen, wie Sie mehrere Decorators verketten, Decorators erstellen, die Parameter akzeptieren, die Metadaten von Funktionen beim Verwenden von Decorators beibehalten und Decorators auf verschiedene Arten von Klassenmethoden anwenden. Die Dateien, mit denen Sie arbeiten werden, sind logcall.py, validate.py und sample.py.
Beibehalten von Funktionsmetadaten in Decorators
In Python sind Decorators (Dekorateure) ein leistungsstarkes Werkzeug, das es Ihnen ermöglicht, das Verhalten von Funktionen zu ändern. Allerdings gibt es ein kleines Problem, wenn Sie einen Decorator verwenden, um eine Funktion zu umhüllen. Standardmäßig gehen die Metadaten der ursprünglichen Funktion, wie ihr Name, ihre Dokumentationszeichenfolge (Docstring) und ihre Anmerkungen (Annotations), verloren. Metadaten sind wichtig, da sie die Introspektion (die Untersuchung der Code-Struktur) und die Generierung von Dokumentation erleichtern. Lassen Sie uns zunächst dieses Problem verifizieren.
Öffnen Sie Ihr Terminal in der WebIDE. Wir werden einige Python-Befehle ausführen, um zu sehen, was passiert, wenn wir einen Decorator verwenden. Die folgenden Befehle erstellen eine einfache Funktion add, die mit einem Decorator umhüllt wird, und geben dann die Funktion und ihren Docstring aus.
cd ~/project
python3 -c "from logcall import logged; @logged
def add(x,y):
'Adds two things'
return x+y
print(add)
print(add.__doc__)"
Wenn Sie diese Befehle ausführen, sehen Sie eine Ausgabe ähnlich dieser:
<function wrapper at 0x...>
None
Beachten Sie, dass anstelle des Funktionsnamens add der Name wrapper angezeigt wird. Und der Docstring, der 'Adds two things' sein sollte, ist None. Dies kann ein großes Problem sein, wenn Sie Tools verwenden, die auf diesen Metadaten basieren, wie Introspektions-Tools oder Dokumentations-Generatoren.
Beheben des Problems mit functools.wraps
Python's functools-Modul kommt uns hier zu Hilfe. Es bietet einen wraps-Decorator, der uns dabei helfen kann, die Funktionsmetadaten zu bewahren. Lassen Sie uns sehen, wie wir unseren logged-Decorator so ändern können, dass er wraps verwendet.
- Öffnen Sie zunächst die Datei
logcall.pyin der WebIDE. Sie können in das Projektverzeichnis navigieren, indem Sie den folgenden Befehl im Terminal ausführen:
cd ~/project
- Aktualisieren Sie nun den
logged-Decorator inlogcall.pymit dem folgenden Code. Der@wraps(func)-Decorator ist hier der Schlüssel. Er kopiert alle Metadaten von der ursprünglichen Funktionfuncauf die Wrapper-Funktion.
from functools import wraps
def logged(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
Der
@wraps(func)-Decorator erledigt eine wichtige Aufgabe. Er nimmt alle Metadaten (wie den Namen, den Docstring und die Anmerkungen) von der ursprünglichen Funktionfuncund fügt sie derwrapper-Funktion hinzu. Auf diese Weise hat die dekorierte Funktion die richtigen Metadaten.Lassen Sie uns unseren verbesserten Decorator testen. Führen Sie die folgenden Befehle im Terminal aus:
python3 -c "from logcall import logged; @logged
def add(x,y):
'Adds two things'
return x+y
print(add)
print(add.__doc__)"
Jetzt sollten Sie sehen:
<function add at 0x...>
Adds two things
Toll! Der Funktionsname und der Docstring werden beibehalten. Dies bedeutet, dass unser Decorator jetzt wie erwartet funktioniert und die Metadaten der ursprünglichen Funktion intakt sind.
Beheben des Problems im validate.py-Decorator
Lassen Sie uns nun dasselbe Verfahren auf den validated-Decorator in validate.py anwenden. Dieser Decorator wird verwendet, um die Typen der Funktionsargumente und den Rückgabewert basierend auf den Anmerkungen der Funktion zu überprüfen.
Öffnen Sie
validate.pyin der WebIDE.Aktualisieren Sie den
validated-Decorator mit dem@wraps-Decorator. Der folgende Code zeigt, wie dies geht. Der@wraps(func)-Decorator wird derwrapper-Funktion innerhalb desvalidated-Decorators hinzugefügt, um die Metadaten zu bewahren.
from functools import wraps
class Integer:
@classmethod
def __instancecheck__(cls, x):
return isinstance(x, int)
def validated(func):
@wraps(func)
def wrapper(*args, **kwargs):
## Get function annotations
annotations = func.__annotations__
## Check arguments against annotations
for arg_name, arg_value in zip(func.__code__.co_varnames, args):
if arg_name in annotations and not isinstance(arg_value, annotations[arg_name]):
raise TypeError(f'Expected {arg_name} to be {annotations[arg_name].__name__}')
## Run the function and get the result
result = func(*args, **kwargs)
## Check the return value
if 'return' in annotations and not isinstance(result, annotations['return']):
raise TypeError(f'Expected return value to be {annotations["return"].__name__}')
return result
return wrapper
- Lassen Sie uns testen, ob unser
validated-Decorator jetzt die Metadaten beibehält. Führen Sie die folgenden Befehle im Terminal aus:
python3 -c "from validate import validated, Integer; @validated
def multiply(x: Integer, y: Integer) -> Integer:
'Multiplies two integers'
return x * y
print(multiply)
print(multiply.__doc__)"
Sie sollten sehen:
<function multiply at 0......>
Multiplies two integers
Jetzt behalten beide Decorators, logged und validated, die Metadaten der Funktionen, die sie dekorieren, ordnungsgemäß bei. Dies stellt sicher, dass die Funktionen, wenn Sie diese Decorators verwenden, immer noch ihre ursprünglichen Namen, Docstrings und Anmerkungen haben, was für die Lesbarkeit und Wartbarkeit des Codes sehr nützlich ist.
Erstellen von Decorators mit Argumenten
Bisher haben wir den @logged-Decorator verwendet, der immer eine feste Nachricht ausgibt. Aber was, wenn Sie das Nachrichtenformat anpassen möchten? In diesem Abschnitt lernen wir, wie man einen neuen Decorator erstellt, der Argumente akzeptieren kann. Dies gibt Ihnen mehr Flexibilität bei der Verwendung von Decorators.
Verständnis von parametrisierten Decorators
Ein parametrisierter Decorator ist eine besondere Art von Funktion. Anstatt direkt eine andere Funktion zu modifizieren, gibt er einen Decorator zurück. Die allgemeine Struktur eines parametrisierten Decorators sieht wie folgt aus:
def decorator_with_args(arg1, arg2, ...):
def actual_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
## Use arg1, arg2, ... here
## Call the original function
return func(*args, **kwargs)
return wrapper
return actual_decorator
Wenn Sie @decorator_with_args(value1, value2) in Ihrem Code verwenden, ruft Python zunächst decorator_with_args(value1, value2) auf. Dieser Aufruf gibt den eigentlichen Decorator zurück, der dann auf die Funktion angewendet wird, die auf die @-Syntax folgt. Dieser zweistufige Prozess ist der Schlüssel für die Funktionsweise von parametrisierten Decorators.
Erstellen des logformat-Decorators
Lassen Sie uns einen @logformat(fmt)-Decorator erstellen, der eine Formatzeichenfolge als Argument nimmt. Dies ermöglicht es uns, die Protokollnachricht anzupassen.
- Öffnen Sie
logcall.pyin der WebIDE und fügen Sie den neuen Decorator hinzu. Der folgende Code zeigt, wie man sowohl den bestehendenlogged-Decorator als auch den neuenlogformat-Decorator definiert:
from functools import wraps
def logged(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
def logformat(fmt):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(fmt.format(func=func))
return func(*args, **kwargs)
return wrapper
return decorator
Im logformat-Decorator nimmt die äußere Funktion logformat eine Formatzeichenfolge fmt als Argument. Sie gibt dann die decorator-Funktion zurück, die der eigentliche Decorator ist, der die Ziel-Funktion modifiziert.
- Jetzt testen wir unseren neuen Decorator, indem wir
sample.pyändern. Der folgende Code zeigt, wie man sowohl denlogged- als auch denlogformat-Decorator auf verschiedene Funktionen anwendet:
from logcall import logged, logformat
@logged
def add(x, y):
"Adds two numbers"
return x + y
@logged
def sub(x, y):
"Subtracts y from x"
return x - y
@logformat('{func.__code__.co_filename}:{func.__name__}')
def mul(x, y):
"Multiplies two numbers"
return x * y
Hier verwenden die Funktionen add und sub den logged-Decorator, während die Funktion mul den logformat-Decorator mit einer benutzerdefinierten Formatzeichenfolge verwendet.
- Führen Sie die aktualisierte Datei
sample.pyaus, um die Ergebnisse zu sehen. Öffnen Sie Ihr Terminal und führen Sie den folgenden Befehl aus:
cd ~/project
python3 -c "import sample; print(sample.add(2, 3)); print(sample.mul(2, 3))"
Sie sollten eine Ausgabe ähnlich der folgenden sehen:
Calling add
5
sample.py:mul
6
Diese Ausgabe zeigt, dass der logged-Decorator wie erwartet den Funktionsnamen ausgibt und der logformat-Decorator die benutzerdefinierte Formatzeichenfolge verwendet, um den Dateinamen und den Funktionsnamen auszugeben.
Neudefinieren des logged-Decorators unter Verwendung von logformat
Jetzt, da wir einen flexibleren logformat-Decorator haben, können wir unseren ursprünglichen logged-Decorator unter Verwendung dieses neuen Decorators neu definieren. Dies hilft uns, Code wiederzuverwenden und ein konsistentes Protokollformat beizubehalten.
- Aktualisieren Sie
logcall.pymit dem folgenden Code:
from functools import wraps
def logformat(fmt):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(fmt.format(func=func))
return func(*args, **kwargs)
return wrapper
return decorator
## Define logged using logformat
logged = lambda func: logformat("Calling {func.__name__}")(func)
Hier verwenden wir eine Lambda-Funktion, um den logged-Decorator in Bezug auf den logformat-Decorator zu definieren. Die Lambda-Funktion nimmt eine Funktion func und wendet den logformat-Decorator mit einer bestimmten Formatzeichenfolge an.
- Testen Sie, ob der neu definierte
logged-Decorator noch funktioniert. Öffnen Sie Ihr Terminal und führen Sie den folgenden Befehl aus:
cd ~/project
python3 -c "from logcall import logged; @logged
def greet(name):
return f'Hello, {name}'
print(greet('World'))"
Sie sollten sehen:
Calling greet
Hello, World
Dies zeigt, dass der neu definierte logged-Decorator wie erwartet funktioniert und wir den logformat-Decorator erfolgreich wiederverwendet haben, um ein konsistentes Protokollformat zu erreichen.
Anwenden von Decorators auf Klassenmethoden
Jetzt werden wir untersuchen, wie Decorators mit Klassenmethoden interagieren. Dies kann etwas tricky sein, da Python verschiedene Arten von Methoden hat: Instanzmethoden, Klassenmethoden, statische Methoden und Properties. Decorators sind Funktionen, die eine andere Funktion als Argument nehmen und das Verhalten dieser Funktion erweitern, ohne sie explizit zu modifizieren. Wenn wir Decorators auf Klassenmethoden anwenden, müssen wir darauf achten, wie sie mit diesen verschiedenen Methodentypen zusammenarbeiten.
Das Problem verstehen
Lassen Sie uns sehen, was passiert, wenn wir unseren @logged-Decorator auf verschiedene Methodentypen anwenden. Der @logged-Decorator wird wahrscheinlich verwendet, um Informationen über Methodenaufrufe zu protokollieren.
- Erstellen Sie eine neue Datei
methods.pyin der WebIDE. Diese Datei wird unsere Klasse mit verschiedenen Methodentypen enthalten, die mit dem@logged-Decorator dekoriert sind.
from logcall import logged
class Spam:
@logged
def instance_method(self):
print("Instance method called")
return "instance result"
@logged
@classmethod
def class_method(cls):
print("Class method called")
return "class result"
@logged
@staticmethod
def static_method():
print("Static method called")
return "static result"
@logged
@property
def property_method(self):
print("Property method called")
return "property result"
In diesem Code haben wir eine Klasse Spam mit vier verschiedenen Methodentypen. Jede Methode ist mit dem @logged-Decorator dekoriert, und einige sind auch mit anderen eingebauten Decorators wie @classmethod, @staticmethod und @property dekoriert.
- Lassen Sie uns testen, wie es funktioniert. Wir werden einen Python-Befehl im Terminal ausführen, um diese Methoden aufzurufen und die Ausgabe zu sehen.
cd ~/project
python3 -c "from methods import Spam; s = Spam(); print(s.instance_method()); print(Spam.class_method()); print(Spam.static_method()); print(s.property_method)"
Wenn Sie diesen Befehl ausführen, werden Sie möglicherweise einige Probleme bemerken:
- Der
@property-Decorator funktioniert möglicherweise nicht richtig mit unserem@logged-Decorator. Der@property-Decorator wird verwendet, um eine Methode als Property zu definieren, und er hat eine spezifische Arbeitsweise. Wenn er mit dem@logged-Decorator kombiniert wird, können Konflikte auftreten. - Die Reihenfolge der Decorators spielt für
@classmethodund@staticmethodeine Rolle. Die Reihenfolge, in der Decorators angewendet werden, kann das Verhalten der Methode ändern.
Die Reihenfolge der Decorators
Wenn Sie mehrere Decorators anwenden, werden sie von unten nach oben angewendet. Dies bedeutet, dass der Decorator, der am nächsten an der Methodendefinition steht, zuerst angewendet wird, und dann werden die darüber liegenden nacheinander angewendet. Beispielsweise:
@decorator1
@decorator2
def func():
pass
Dies ist äquivalent zu:
func = decorator1(decorator2(func))
In diesem Beispiel wird decorator2 zuerst auf func angewendet, und dann wird decorator1 auf das Ergebnis von decorator2(func) angewendet.
Die Decorator-Reihenfolge korrigieren
Lassen Sie uns unsere methods.py-Datei aktualisieren, um die Decorator-Reihenfolge zu korrigieren. Indem wir die Reihenfolge der Decorators ändern, können wir sicherstellen, dass jede Methode wie erwartet funktioniert.
from logcall import logged
class Spam:
@logged
def instance_method(self):
print("Instance method called")
return "instance result"
@classmethod
@logged
def class_method(cls):
print("Class method called")
return "class result"
@staticmethod
@logged
def static_method():
print("Static method called")
return "static result"
@property
@logged
def property_method(self):
print("Property method called")
return "property result"
In dieser aktualisierten Version:
- Bei
instance_methodspielt die Reihenfolge keine Rolle. Instanzmethoden werden auf einer Instanz der Klasse aufgerufen, und der@logged-Decorator kann in beliebiger Reihenfolge angewendet werden, ohne die grundlegende Funktionalität zu beeinträchtigen. - Bei
class_methodwenden wir@classmethodnach@loggedan. Der@classmethod-Decorator ändert die Art und Weise, wie die Methode aufgerufen wird, und das Anwenden nach@loggedstellt sicher, dass die Protokollierung korrekt funktioniert. - Bei
static_methodwenden wir@staticmethodnach@loggedan. Ähnlich wie bei@classmethodhat der@staticmethod-Decorator sein eigenes Verhalten, und die Reihenfolge mit dem@logged-Decorator muss korrekt sein. - Bei
property_methodwenden wir@propertynach@loggedan. Dies stellt sicher, dass das Property-Verhalten beibehalten wird, während gleichzeitig die Protokollierungsfunktion erhalten bleibt.
- Lassen Sie uns den aktualisierten Code testen. Wir werden den gleichen Befehl wie zuvor ausführen, um zu sehen, ob die Probleme behoben sind.
cd ~/project
python3 -c "from methods import Spam; s = Spam(); print(s.instance_method()); print(Spam.class_method()); print(Spam.static_method()); print(s.property_method)"
Jetzt sollten Sie für alle Methodentypen eine korrekte Protokollierung sehen:
Calling instance_method
Instance method called
instance result
Calling class_method
Class method called
class result
Calling static_method
Static method called
static result
Calling property_method
Property method called
property result
Best Practices für Methoden-Decorators
Beim Arbeiten mit Methoden-Decorators sollten Sie die folgenden Best Practices befolgen:
- Wenden Sie Methoden-transformierende Decorators (
@classmethod,@staticmethod,@property) nach Ihren benutzerdefinierten Decorators an. Dies stellt sicher, dass die benutzerdefinierten Decorators zuerst ihre Protokollierung oder andere Operationen ausführen können, und dann können die eingebauten Decorators die Methode wie beabsichtigt transformieren. - Beachten Sie, dass die Ausführung der Decorators zur Klassen-Definition erfolgt, nicht zum Methoden-Aufruf. Dies bedeutet, dass jeder Setup- oder Initialisierungscode im Decorator ausgeführt wird, wenn die Klasse definiert wird, nicht wenn die Methode aufgerufen wird.
- Bei komplexeren Fällen müssen Sie möglicherweise spezialisierte Decorators für verschiedene Methodentypen erstellen. Verschiedene Methodentypen haben unterschiedliches Verhalten, und ein "einer passt für alle"-Decorator funktioniert möglicherweise nicht in allen Situationen.
Erstellen eines Typüberprüfungs-Decorators mit Argumenten
In den vorherigen Schritten haben wir uns mit dem @validated-Decorator beschäftigt. Dieser Decorator wird verwendet, um Typannotationen in Python-Funktionen zu erzwingen. Typannotationen sind eine Möglichkeit, die erwarteten Typen von Funktionsargumenten und Rückgabewerten anzugeben. Jetzt gehen wir einen Schritt weiter. Wir werden einen flexibleren Decorator erstellen, der Typangaben als Argumente akzeptieren kann. Dies bedeutet, dass wir die Typen, die wir für jedes Argument und den Rückgabewert wünschen, auf eine explizitere Weise definieren können.
Das Ziel verstehen
Unser Ziel ist es, einen @enforce()-Decorator zu erstellen. Dieser Decorator ermöglicht es uns, Typbeschränkungen mithilfe von Schlüsselwortargumenten anzugeben. Hier ist ein Beispiel, wie es funktioniert:
@enforce(x=Integer, y=Integer, return_=Integer)
def add(x, y):
return x + y
In diesem Beispiel verwenden wir den @enforce-Decorator, um anzugeben, dass die Argumente x und y der Funktion add vom Typ Integer sein sollten und auch der Rückgabewert vom Typ Integer sein sollte. Dieser Decorator verhält sich ähnlich wie unser vorheriger @validated-Decorator, aber er gibt uns mehr Kontrolle über die Typangaben.
Erstellen des enforce-Decorators
- Öffnen Sie zunächst die Datei
validate.pyin der WebIDE. Wir werden unseren neuen Decorator zu dieser Datei hinzufügen. Hier ist der Code, den wir hinzufügen werden:
from functools import wraps
class Integer:
@classmethod
def __instancecheck__(cls, x):
return isinstance(x, int)
def validated(func):
@wraps(func)
def wrapper(*args, **kwargs):
## Get function annotations
annotations = func.__annotations__
## Check arguments against annotations
for arg_name, arg_value in zip(func.__code__.co_varnames, args):
if arg_name in annotations and not isinstance(arg_value, annotations[arg_name]):
raise TypeError(f'Expected {arg_name} to be {annotations[arg_name].__name__}')
## Run the function and get the result
result = func(*args, **kwargs)
## Check the return value
if 'return' in annotations and not isinstance(result, annotations['return']):
raise TypeError(f'Expected return value to be {annotations["return"].__name__}')
return result
return wrapper
def enforce(**type_specs):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
## Check argument types
for arg_name, arg_value in zip(func.__code__.co_varnames, args):
if arg_name in type_specs and not isinstance(arg_value, type_specs[arg_name]):
raise TypeError(f'Expected {arg_name} to be {type_specs[arg_name].__name__}')
## Run the function and get the result
result = func(*args, **kwargs)
## Check the return value
if 'return_' in type_specs and not isinstance(result, type_specs['return_']):
raise TypeError(f'Expected return value to be {type_specs["return_"].__name__}')
return result
return wrapper
return decorator
Lassen Sie uns analysieren, was dieser Code tut. Die Klasse Integer wird verwendet, um einen benutzerdefinierten Typ zu definieren. Der validated-Decorator überprüft die Typen der Funktionsargumente und des Rückgabewerts anhand der Typannotationen der Funktion. Der enforce-Decorator ist der neue, den wir erstellen. Er nimmt Schlüsselwortargumente entgegen, die die Typen für jedes Argument und den Rückgabewert angeben. Innerhalb der wrapper-Funktion des enforce-Decorators überprüfen wir, ob die Typen der Argumente und des Rückgabewerts den angegebenen Typen entsprechen. Wenn nicht, werfen wir einen TypeError.
- Jetzt testen wir unseren neuen
@enforce-Decorator. Wir werden einige Testfälle ausführen, um zu sehen, ob er wie erwartet funktioniert. Hier ist der Code, um die Tests auszuführen:
cd ~/project
python3 -c "from validate import enforce, Integer
@enforce(x=Integer, y=Integer, return_=Integer)
def add(x, y):
return x + y
## This should work
print(add(2, 3))
## This should raise a TypeError
try:
print(add('2', 3))
except TypeError as e:
print(f'Error: {e}')
## This should raise a TypeError
try:
@enforce(x=Integer, y=Integer, return_=Integer)
def bad_add(x, y):
return str(x + y)
print(bad_add(2, 3))
except TypeError as e:
print(f'Error: {e}')"
In diesem Testcode definieren wir zunächst eine add-Funktion mit dem @enforce-Decorator. Dann rufen wir die add-Funktion mit gültigen Argumenten auf, was ohne Fehler funktionieren sollte. Als nächstes rufen wir die add-Funktion mit einem ungültigen Argument auf, was einen TypeError auslösen sollte. Schließlich definieren wir eine bad_add-Funktion, die einen Wert vom falschen Typ zurückgibt, was ebenfalls einen TypeError auslösen sollte.
Wenn Sie diesen Testcode ausführen, sollten Sie eine Ausgabe ähnlich der folgenden sehen:
5
Error: Expected x to be Integer
Error: Expected return value to be Integer
Diese Ausgabe zeigt, dass unser @enforce-Decorator korrekt funktioniert. Er wirft einen TypeError, wenn die Typen der Argumente oder des Rückgabewerts nicht den angegebenen Typen entsprechen.
Vergleich der beiden Ansätze
Sowohl der @validated- als auch der @enforce-Decorator erreichen dasselbe Ziel, nämlich die Erzwingung von Typbeschränkungen, aber sie tun dies auf verschiedene Weise.
Der
@validated-Decorator verwendet die eingebauten Typannotationen von Python. Hier ist ein Beispiel:@validated def add(x: Integer, y: Integer) -> Integer: return x + yMit diesem Ansatz geben wir die Typen direkt in der Funktionsdefinition mithilfe von Typannotationen an. Dies ist ein eingebautes Feature von Python und bietet bessere Unterstützung in integrierten Entwicklungsumgebungen (IDEs). IDEs können diese Typannotationen nutzen, um Codevervollständigung, Typüberprüfung und andere nützliche Funktionen bereitzustellen.
Der
@enforce-Decorator hingegen verwendet Schlüsselwortargumente, um die Typen anzugeben. Hier ist ein Beispiel:@enforce(x=Integer, y=Integer, return_=Integer) def add(x, y): return x + yDieser Ansatz ist expliziter, da wir die Typangaben direkt als Argumente an den Decorator übergeben. Er kann nützlich sein, wenn man mit Bibliotheken arbeitet, die auf anderen Annotationssystemen beruhen.
Jeder Ansatz hat seine eigenen Vorteile. Typannotationen sind ein nativer Bestandteil von Python und bieten bessere IDE-Unterstützung, während der @enforce-Ansatz uns mehr Flexibilität und Explizitheit gibt. Sie können den Ansatz wählen, der am besten zu Ihren Bedürfnissen passt, je nachdem, an welchem Projekt Sie arbeiten.
Zusammenfassung
In diesem Lab haben Sie gelernt, wie Sie Decorators effektiv erstellen und nutzen können. Sie haben gelernt, die Metadaten von Funktionen mit functools.wraps zu bewahren, Decorators zu erstellen, die Parameter akzeptieren, mehrere Decorators zu verarbeiten und deren Anwendungsreihenfolge zu verstehen. Sie haben auch gelernt, Decorators auf verschiedene Klassenmethoden anzuwenden und einen Typüberprüfungs-Decorator zu erstellen, der Argumente akzeptiert.
Diese Decorator-Muster werden häufig in Python-Frameworks wie Flask, Django und pytest verwendet. Das Beherrschen von Decorators ermöglicht es Ihnen, wartbareren und wiederverwendbaren Code zu schreiben. Um Ihr Lernen fortzusetzen, können Sie Context Manager (Kontextmanager), klassenbasierte Decorators, die Verwendung von Decorators für das Caching und die erweiterte Typüberprüfung mit Decorators erkunden.