En guide till Python-dekoratörer för avancerade programmerare

Python-dekoratörer är ett kraftfullt och flexibelt verktyg för att modifiera beteendet hos funktioner eller metoder. De tillåter programmerare att utöka eller ändra funktionaliteten hos anropsbara objekt på ett rent, läsbart och underhållbart sätt. Den här artikeln utforskar avancerade koncept relaterade till Python-dekoratörer, inklusive kapslade dekoratörer, dekoratörsargument och klassbaserade dekoratörer.

Vad är dekoratörer?

Dekoratörer är funktioner som ändrar beteendet hos en annan funktion. De slår in en annan funktion för att utöka dess beteende utan att explicit ändra dess kod. Dekoratorer definieras med syntaxen @decorator_name och placeras ovanför funktionsdefinitionen.

Grundläggande dekorationssyntax

En enkel dekoratör tar en funktion som ett argument, definierar en inre funktion som lägger till något beteende och returnerar sedan den inre funktionen.

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

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

Dekoratorfunktioner med argument

Dekoratörer kan vara mer flexibla genom att acceptera argument. För att skapa en sådan dekoratör måste du skriva en funktion som returnerar en dekoratör. Detta gör det möjligt att lägga till ett mer dynamiskt beteende för dekoratörerna.

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

Häckande dekoratörer

Dekoratörer kan kapslas för att kombinera flera beteenden. Vi kan till exempel använda två eller flera dekoratörer på en enda funktion.

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def repeat_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + result
    return wrapper

@repeat_decorator
@uppercase_decorator
def say_word(word):
    return word

print(say_word("hello"))

Klassbaserade dekoratörer

I Python kan dekoratörer också implementeras som klasser genom att använda metoden __call__. Klassbaserade dekoratörer är användbara när du behöver mer komplex statlig förvaltning och beteende.

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()
say_hello()

Använd functools.wraps för att bevara metadata

När du skriver dekoratörer förlorar den dekorerade funktionen sin ursprungliga metadata, som dess namn och docstring. functools.wraps dekoratorn kan användas för att kopiera originalfunktionens metadata till omslagsfunktionen.

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Wrapper function executed before", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def display_info(name, age):
    """Displays name and age."""
    print(f"display_info ran with arguments ({name}, {age})")

print(display_info.__name__)  # Output: display_info
print(display_info.__doc__)   # Output: Displays name and age.

Slutsats

Python-dekoratörer är en kraftfull funktion som möjliggör flexibel koddesign och beteendemodifiering. Avancerad användning, såsom kapslade dekoratörer, dekoratörer med argument och klassbaserade dekoratörer, kan ge ännu mer funktionalitet och läsbarhet till Python-program. Genom att förstå och använda dekoratörer korrekt kan utvecklare skriva mer kortfattad, effektiv och läsbar kod.