En guide til Python-dekoratører for avanserte programmerere

Python-dekoratorer er et kraftig og fleksibelt verktøy for å endre oppførselen til funksjoner eller metoder. De lar programmerere utvide eller endre funksjonaliteten til anropbare objekter på en ren, lesbar og vedlikeholdbar måte. Denne artikkelen utforsker avanserte konsepter relatert til Python-dekoratører, inkludert nestede dekoratører, dekoratørargumenter og klassebaserte dekoratører.

Hva er dekoratører?

Dekoratører er funksjoner som endrer oppførselen til en annen funksjon. De pakker inn en annen funksjon for å utvide oppførselen uten eksplisitt å endre koden. Dekoratorer er definert ved å bruke @decorator_name-syntaksen og plasseres over funksjonsdefinisjonen.

Grunnleggende dekorasjonssyntaks

En enkel dekoratør tar en funksjon som et argument, definerer en indre funksjon som legger til noe atferd, og returnerer deretter den indre funksjonen.

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

Dekoratorfunksjoner med argumenter

Dekoratører kan være mer fleksible ved å akseptere argumenter. For å lage en slik dekoratør må du skrive en funksjon som returnerer en dekoratør. Dette gjør det mulig å legge til mer dynamisk oppførsel til dekoratørene.

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

Hekkedekoratører

Dekoratører kan settes sammen for å kombinere flere oppførsel. For eksempel kan vi bruke to eller flere dekoratører på en enkelt funksjon.

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

Klassebaserte dekoratører

I Python kan dekoratører også implementeres som klasser ved å bruke __call__ metoden. Klassebaserte dekoratører er nyttige når du trenger mer kompleks statlig ledelse og oppførsel.

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

Bruk av functools.wraps for å bevare metadata

Når du skriver dekoratorer, mister den dekorerte funksjonen sine originale metadata, for eksempel navnet og dokumentstrengen. functools.wraps dekoratoren kan brukes til å kopiere metadataene til den opprinnelige funksjonen til wrapper-funksjonen.

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.

Konklusjon

Python-dekoratorer er en kraftig funksjon som muliggjør fleksibel kodedesign og atferdsendring. Avansert bruk, som nestede dekoratører, dekoratører med argumenter og klassebaserte dekoratører, kan gi enda mer funksjonalitet og lesbarhet til Python-programmer. Ved å forstå og bruke dekoratører riktig, kan utviklere skrive mer kortfattet, effektiv og lesbar kode.