Przewodnik po dekoratorach Pythona dla zaawansowanych programistów

Dekoratory Pythona to potężne i elastyczne narzędzie do modyfikowania zachowania funkcji lub metod. Umożliwiają programistom rozszerzanie lub zmienianie funkcjonalności obiektów wywoływalnych w czysty, czytelny i łatwy w utrzymaniu sposób. W tym artykule omówiono zaawansowane koncepcje związane z dekoratorami Pythona, w tym zagnieżdżone dekoratory, argumenty dekoratora i dekoratory oparte na klasach.

Czym są dekoratory?

Dekoratory to funkcje, które modyfikują zachowanie innej funkcji. Owijają inną funkcję, aby rozszerzyć jej zachowanie bez jawnej modyfikacji jej kodu. Dekoratory są definiowane za pomocą składni @decorator_name i są umieszczane nad definicją funkcji.

Podstawowa składnia dekoratora

Prosty dekorator przyjmuje funkcję jako argument, definiuje funkcję wewnętrzną, która dodaje pewne zachowanie, a następnie zwraca funkcję wewnętrzną.

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

Funkcje dekoratora z argumentami

Dekoratory mogą być bardziej elastyczne, akceptując argumenty. Aby utworzyć taki dekorator, musisz napisać funkcję, która zwraca dekorator. Umożliwia to dodanie bardziej dynamicznego zachowania do dekoratorów.

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

Dekoratory zagnieżdżające

Dekoratory mogą być zagnieżdżane, aby łączyć wiele zachowań. Na przykład możemy używać dwóch lub więcej dekoratorów w jednej funkcji.

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

Dekoratory oparte na klasach

W Pythonie dekoratory mogą być również implementowane jako klasy za pomocą metody __call__. Dekoratory oparte na klasach są przydatne, gdy potrzebujesz bardziej złożonego zarządzania stanem i zachowaniem.

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

Używanie functools.wraps do zachowania metadanych

Gdy piszesz dekoratory, udekorowana funkcja traci swoje oryginalne metadane, takie jak nazwa i docstring. Dekorator functools.wraps może być użyty do skopiowania metadanych oryginalnej funkcji do funkcji opakowującej.

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.

Wniosek

Dekoratory Pythona to potężna funkcja, która umożliwia elastyczne projektowanie kodu i modyfikację zachowania. Zaawansowane użycie, takie jak zagnieżdżone dekoratory, dekoratory z argumentami i dekoratory oparte na klasach, może zapewnić jeszcze większą funkcjonalność i czytelność programów Pythona. Dzięki zrozumieniu i prawidłowemu wykorzystaniu dekoratorów programiści mogą pisać bardziej zwięzły, wydajny i czytelny kod.