Heim > Backend-Entwicklung > Python-Tutorial > Python-Dekoratoren: Ein umfassender Leitfaden

Python-Dekoratoren: Ein umfassender Leitfaden

Barbara Streisand
Freigeben: 2025-01-05 22:53:43
Original
795 Leute haben es durchsucht

Python Decorators: A Comprehensive Guide

Als ich anfing, mit Python zu programmieren, war die Version, wenn ich mich nicht irre, 3.3. Als ich mit dem Programmieren begann, standen der Python-Community daher schon lange Dekoratoren zur Verfügung.

Funktionsdekoratoren kamen mit Version 2.2 zu Python und Klassendekoratoren kamen mit Version 2.6 zu Python.

Ich persönlich halte die Decorator-Funktion von Python für eine sehr leistungsstarke Funktion der Sprache.

Eigentlich ist es mein Ziel, eine Reihe von Artikeln über die am schwierigsten zu verstehenden Themen in Python zu verfassen. Ich habe vor, diese Themen, die etwas mehr als zehn sind, einzeln zu behandeln.

In diesem Artikel werde ich versuchen, jeden Teil des Themas Dekorateure so weit wie möglich anzusprechen.

1. Historischer Kontext

  • Anfänge (Pre-Python 2.2): Vor Dekoratoren erforderte das Ändern von Funktionen oder Klassen oft manuelles Umschließen oder Monkey-Patching, was umständlich und weniger lesbar war.
  • Metaklassen (Python 2.2): Metaklassen boten eine Möglichkeit, die Klassenerstellung zu steuern und boten einige der Funktionen, die Dekoratoren später bereitstellen würden, waren jedoch für einfache Änderungen komplex.
  • PEP 318 (Python 2.4): Dekoratoren wurden offiziell in Python 2.4 durch PEP 318 eingeführt. Der Vorschlag wurde von Annotationen in Java inspiriert und zielte darauf ab, eine sauberere, deklarativere Möglichkeit zum Ändern von Funktionen und Methoden bereitzustellen .
  • Klassendekoratoren (Python 2.6): Python 2.6 hat die Unterstützung von Dekoratoren für Klassen erweitert und so deren Vielseitigkeit weiter verbessert.
  • Weit verbreitete Akzeptanz: Dekoratoren wurden schnell zu einer beliebten Funktion, die in Frameworks wie Flask und Django häufig für Routing, Authentifizierung und mehr verwendet wird.

2. Was sind Dekorateure?

Im Wesentlichen ist ein Dekorator ein Entwurfsmuster in Python, mit dem Sie das Verhalten einer Funktion oder einer Klasse ändern können, ohne deren Kernstruktur zu ändern. Dekoratoren sind eine Form der Metaprogrammierung, bei der Sie im Wesentlichen Code schreiben, der anderen Code manipuliert.

Sie wissen, dass Python Namen mithilfe des in der folgenden Reihenfolge angegebenen Bereichs auflöst:

  1. Lokal
  2. Umschließen
  3. Global
  4. Eingebaut

Dekorateure sind ein umschließender Bereich, der eng mit dem Verschlusskonzept zusammenhängt.

Schlüsselidee: Ein Dekorateur nimmt eine Funktion als Eingabe, fügt ihr einige Funktionen hinzu und gibt eine modifizierte Funktion zurück.

Analogie: Stellen Sie sich einen Dekorateur als Geschenkverpackung vor. Sie haben ein Geschenk (die ursprüngliche Funktion) und verpacken es mit dekorativem Papier (der Dekorateur), um es schöner aussehen zu lassen oder zusätzliche Funktionen hinzuzufügen (z. B. eine Schleife oder eine Karte). Das darin enthaltene Geschenk bleibt dasselbe, aber seine Präsentation oder die damit verbundenen Aktionen werden verbessert.

A) Dekorator-Variationen: funktionsbasiert vs. klassenbasiert

Die meisten Dekoratoren in Python werden mithilfe von Funktionen implementiert, Sie können Dekoratoren jedoch auch mithilfe von Klassen erstellen.
Funktionsbasierte Dekoratoren sind häufiger und einfacher, während klassenbasierte Dekoratoren zusätzliche Flexibilität bieten.

Funktionsbasierte grundlegende Decorator-Syntax

def my_decorator(func):
    def wrapper(*args, **kwargs):
        # Do something before calling the decorated function
        print("Before function call")
        result = func(*args, **kwargs)
        # Do something after calling the decorated function
        print("After function call")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("World")
Nach dem Login kopieren
Nach dem Login kopieren

Erklärung:

  • my_decorator ist die Dekoratorfunktion. Es benötigt die zu dekorierende Funktion func als Eingabe.
  • Wrapper ist eine innere Funktion, die den Aufruf der ursprünglichen Funktion umschließt. Es kann Code vor und nach der ursprünglichen Funktion ausführen.
  • @my_decorator ist die Decorator-Syntax. Es entspricht say_hello = my_decorator(say_hello).

Klassenbasierte grundlegende Decorator-Syntax

Diese verwenden Klassen anstelle von Funktionen, um Dekoratoren zu definieren.

class MyDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        # Do something before calling the decorated function
        print("Before function call")
        result = self.func(*args, **kwargs)
        # Do something after calling the decorated function
        print("After function call")
        return result

@MyDecorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("World")
Nach dem Login kopieren
Nach dem Login kopieren

Erklärung:

  • MyDecorator ist eine Klasse, die als Dekorateur fungiert.
  • Die __init__-Methode speichert die zu dekorierende Funktion.
  • Die Methode __call__ macht die Klasseninstanz aufrufbar, sodass sie wie eine Funktion verwendet werden kann.

B) Implementierung eines einfachen Dekorators

Das grundlegende Konzept von Dekoratoren besteht darin, dass es sich um Funktionen handelt, die eine andere Funktion als Argument verwenden und deren Verhalten erweitern, ohne sie explizit zu ändern.
Hier ist die einfachste Form:

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

# Using the decorator with @ syntax
@my_decorator
def say_hello():
    print("Hello!")

# When we call say_hello()
say_hello()

# This is equivalent to:
# say_hello = my_decorator(say_hello)
Nach dem Login kopieren
Nach dem Login kopieren

C) Implementieren eines Dekorators mit Argumenten

Lassen Sie uns einen Dekorator erstellen, der die Ausführungszeit einer Funktion protokolliert:

def decorator_with_args(func):
    def wrapper(*args, **kwargs):    # Accept any number of arguments
        print(f"Arguments received: {args}, {kwargs}")
        return func(*args, **kwargs)  # Pass arguments to the original function
    return wrapper

@decorator_with_args
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Alice", greeting="Hi")  # Prints arguments then "Hi, Alice!"
Nach dem Login kopieren
Nach dem Login kopieren

D) Implementierung eines parametrisierten Dekorators

Dies sind Dekorateure, die ihre eigenen Parameter akzeptieren können:

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"Hello {name}")
    return "Done"

greet("Bob")  # Prints "Hello Bob" three times
Nach dem Login kopieren

E) Implementierung eines Klassendekorators

def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class DatabaseConnection:
    def __init__(self):
        print("Initializing database connection")

# Creating multiple instances actually returns the same instance
db1 = DatabaseConnection()  # Prints initialization
db2 = DatabaseConnection()  # No initialization printed
print(db1 is db2)  # True
Nach dem Login kopieren

F) Implementieren von Methodendekoratoren

Diese sind speziell für Klassenmethoden konzipiert:

def debug_method(func):
    def wrapper(self, *args, **kwargs):
        print(f"Calling method {func.__name__} of {self.__class__.__name__}")
        return func(self, *args, **kwargs)
    return wrapper

class MyClass:
    @debug_method
    def my_method(self, x, y):
        return x + y

obj = MyClass()
print(obj.my_method(5, 3))

Nach dem Login kopieren

G) Implementieren der Decorator-Verkettung

Mehrere Dekoratoren können auf eine einzelne Funktion angewendet werden:

def bold(func):
    def wrapper():
        return "<b>" + func() + "</b>"
    return wrapper

def italic(func):
    def wrapper():
        return "<i>" + func() + "</i>"
    return wrapper

@bold
@italic
def greet():
    return "Hello!"

print(greet())  # Outputs: <b><i>Hello!</i></b>
Nach dem Login kopieren

Erklärung:

  • Dekorationen werden von unten nach oben aufgetragen.
  • Es ist eher so, wie wir es in der Mathematik machen: f(g(x)).
  • Zuerst wird Kursivschrift und dann Fettschrift angewendet.

H) Was passiert, wenn wir @functools.wraps nicht verwenden?

Der functools.wraps-Dekorator, siehe Dokumente, ist eine Hilfsfunktion, die die Metadaten der ursprünglichen Funktion (wie Name, Dokumentzeichenfolge und Signatur) beibehält, wenn Sie sie mit einem Dekorator umschließen. Wenn Sie es nicht verwenden, gehen diese wichtigen Informationen verloren.

Beispiel:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        """Wrapper docstring"""
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def my_function():
    """My function docstring"""
    pass

print(my_function.__name__)
print(my_function.__doc__)
Nach dem Login kopieren

Ausgabe:

wrapper
Wrapper docstring
Nach dem Login kopieren

Problem:

  • Der Name der ursprünglichen Funktion (my_function) und die Dokumentzeichenfolge („Dokumentzeichenfolge meiner Funktion“) gehen verloren.
  • Dies kann das Debuggen und die Selbstbeobachtung erschweren.

Lösung: Verwenden Sie functools.wraps):

def my_decorator(func):
    def wrapper(*args, **kwargs):
        # Do something before calling the decorated function
        print("Before function call")
        result = func(*args, **kwargs)
        # Do something after calling the decorated function
        print("After function call")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("World")
Nach dem Login kopieren
Nach dem Login kopieren

Ausgabe:

class MyDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        # Do something before calling the decorated function
        print("Before function call")
        result = self.func(*args, **kwargs)
        # Do something after calling the decorated function
        print("After function call")
        return result

@MyDecorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("World")
Nach dem Login kopieren
Nach dem Login kopieren

Vorteile von functools.wraps:

  • Behält Funktionsmetadaten bei.
  • Verbessert die Lesbarkeit und Wartbarkeit des Codes.
  • Erleichtert das Debuggen.
  • Hilft bei Selbstbeobachtungstools und Dokumentationsgeneratoren.

I) Dekorateure mit Staat

Dekoratoren können den Status auch zwischen Funktionsaufrufen beibehalten. Dies ist besonders nützlich für Szenarien wie Caching oder Zählen von Funktionsaufrufen.

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

# Using the decorator with @ syntax
@my_decorator
def say_hello():
    print("Hello!")

# When we call say_hello()
say_hello()

# This is equivalent to:
# say_hello = my_decorator(say_hello)
Nach dem Login kopieren
Nach dem Login kopieren

Ausgabe:

def decorator_with_args(func):
    def wrapper(*args, **kwargs):    # Accept any number of arguments
        print(f"Arguments received: {args}, {kwargs}")
        return func(*args, **kwargs)  # Pass arguments to the original function
    return wrapper

@decorator_with_args
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Alice", greeting="Hi")  # Prints arguments then "Hi, Alice!"
Nach dem Login kopieren
Nach dem Login kopieren

Erklärung:

Die Wrapper-Funktion verwaltet einen Zähler (Aufrufe), der jedes Mal erhöht wird, wenn die dekorierte Funktion aufgerufen wird.
Dies ist ein einfaches Beispiel dafür, wie Dekoratoren verwendet werden können, um den Zustand aufrechtzuerhalten.

J) Best Practices für Python-Dekorateure

  • Verwenden Sie functools.wraps: Verwenden Sie in Ihren Dekoratoren immer @functools.wraps, um die Metadaten der ursprünglichen Funktion beizubehalten.
  • Halten Sie Dekorateure einfach: Dekorateure sollten idealerweise eine bestimmte Sache tun und es gut machen. Dadurch sind sie wiederverwendbar und leichter zu verstehen.
  • Dokumentieren Sie Ihre Dekorateure: Erklären Sie, was Ihr Dekorateur tut, welche Argumente er benötigt und was er zurückgibt.
  • Testen Sie Ihre Dekorateure: Schreiben Sie Unit-Tests, um sicherzustellen, dass Ihre Dekorateure in verschiedenen Szenarien wie erwartet funktionieren.
  • Berücksichtigen Sie die Reihenfolge der Verkettung: Achten Sie beim Verketten mehrerer Dekoratoren auf die Reihenfolge, da diese sich auf den Ausführungsfluss auswirkt.

K) Schlechte Implementierungen (Anti-Patterns)

  • Zu komplexe Dekoratoren: Vermeiden Sie es, zu komplexe Dekoratoren zu erstellen oder zu viele Dinge zu tun. Dadurch sind sie schwer zu verstehen, zu warten und zu debuggen.
  • Ignorieren von functools.wraps: Das Vergessen, @functools.wraps zu verwenden, führt zum Verlust von Funktionsmetadaten, was zu Problemen bei der Selbstbeobachtung und beim Debuggen führen kann.
  • Nebenwirkungen: Dekorateure sollten im Idealfall keine unbeabsichtigten Nebenwirkungen haben, die über die Änderung der dekorierten Funktion hinausgehen.
  • Hardcodierung von Werten: Vermeiden Sie die Hardcodierung von Werten innerhalb von Dekoratoren. Verwenden Sie stattdessen Decorator-Fabriken, um sie konfigurierbar zu machen.
  • Argumente werden nicht ordnungsgemäß behandelt: Stellen Sie sicher, dass Ihre Wrapper-Funktion eine beliebige Anzahl von Positions- und Schlüsselwortargumenten mit *args und **kwargs verarbeiten kann, wenn der Dekorator mit einer Vielzahl von Funktionen verwendet werden soll.

L) 10. Anwendungsfälle aus der Praxis

  • Protokollierung: Aufzeichnen von Funktionsaufrufen, Argumenten und Rückgabewerten zum Debuggen oder Überwachen.
  • Timing: Messung der Ausführungszeit von Funktionen zur Leistungsanalyse.
  • Caching: Speichern der Ergebnisse teurer Funktionsaufrufe, um redundante Berechnungen zu vermeiden (Memoisierung).
  • Authentifizierung und Autorisierung: Überprüfung der Benutzeranmeldeinformationen oder -berechtigungen vor dem Ausführen einer Funktion.
  • Eingabevalidierung:Überprüfen, ob die an eine Funktion übergebenen Argumente bestimmte Kriterien erfüllen.
  • Ratenbegrenzung: Steuerung der Häufigkeit, mit der eine Funktion innerhalb eines bestimmten Zeitraums aufgerufen werden kann.
  • Wiederholungslogik: Automatischer Wiederholungsversuch eines Funktionsaufrufs, wenn dieser aufgrund eines vorübergehenden Fehlers fehlschlägt.
  • Framework-spezifische Aufgaben: Frameworks wie Flask und Django verwenden Dekoratoren für das Routing (Zuordnung von URLs zu Funktionen), die Registrierung von Plugins und mehr.

M) Kuratierte Listen von Python-Dekoratoren

Eine kuratierte Liste von Python-Dekoratoren finden Sie unten:

  • Tolle Python-Dekoratoren
  • Python Decorator-Bibliothek

N) 11. Fazit

Dekoratoren sind eine leistungsstarke und elegante Funktion in Python, mit der Sie Funktionen und Klassen auf saubere und deklarative Weise verbessern können.
Wenn Sie die Prinzipien, Best Practices und potenziellen Fallstricke verstehen, können Sie Dekoratoren effektiv nutzen, um modulareren, wartbareren und aussagekräftigeren Code zu schreiben.
Sie sind ein wertvolles Werkzeug im Arsenal jedes Python-Programmierers, insbesondere wenn er mit Frameworks arbeitet oder wiederverwendbare Komponenten erstellt.

Das obige ist der detaillierte Inhalt vonPython-Dekoratoren: Ein umfassender Leitfaden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
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
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage