En guide til Python-dekoratører for avancerede programmører

Python-dekoratorer er et kraftfuldt og fleksibelt værktøj til at ændre funktioners eller metoders adfærd. De giver programmører mulighed for at udvide eller ændre funktionaliteten af ​​kaldbare objekter på en ren, læsbar og vedligeholdelig måde. Denne artikel udforsker avancerede koncepter relateret til Python-dekoratører, herunder indlejrede dekoratører, dekoratørargumenter og klassebaserede dekoratører.

Hvad er dekoratører?

Dekoratører er funktioner, der ændrer en anden funktions adfærd. De ombryder en anden funktion for at udvide dens adfærd uden eksplicit at ændre dens kode. Dekoratorer defineres ved hjælp af @decorator_name syntaksen og placeres over funktionsdefinitionen.

Grundlæggende dekorationssyntaks

En simpel dekoratør tager en funktion som et argument, definerer en indre funktion, der tilføjer noget adfærd, og returnerer derefter den indre funktion.

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 argumenter

Dekoratører kan være mere fleksible ved at acceptere argumenter. For at oprette en sådan dekoratør skal du skrive en funktion, der returnerer en dekoratør. Dette gør det muligt at tilføje mere dynamisk adfærd til dekoratørerne.

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")

Redende dekoratører

Dekoratører kan indlejres for at kombinere flere adfærd. For eksempel kan vi bruge to eller flere dekoratører på en enkelt 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"))

Klassebaserede dekoratører

I Python kan dekoratører også implementeres som klasser ved at bruge __call__ metoden. Klassebaserede dekoratører er nyttige, når du har brug for mere kompleks statsstyring og adfærd.

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()

Brug af functools.wraps til at bevare metadata

Når du skriver dekoratorer, mister den dekorerede funktion sine originale metadata, såsom dens navn og docstring. functools.wraps dekoratoren kan bruges til at kopiere metadataene for den originale funktion til indpakningsfunktionen.

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.

Konklusion

Python-dekoratorer er en kraftfuld funktion, der giver mulighed for fleksibel kodedesign og adfærdsændring. Avanceret brug, såsom indlejrede dekoratører, dekoratører med argumenter og klassebaserede dekoratører, kan give endnu mere funktionalitet og læsbarhed til Python-programmer. Ved at forstå og bruge dekoratører korrekt kan udviklere skrive mere kortfattet, effektiv og læsbar kode.