Jak tworzyć i używać dekoratorów w Pythonie

Dowiedz się, jak pisać i stosować dekoratory w Pythonie. Opanuj funkcje owijania, modyfikowania zachowania, obsługi argumentów i zachowywania metadanych za pomocą zawijania.

Wypróbuj to rozwiązanie w Edytorze

Przegląd

Dekoratory to jedna z najbardziej eleganckich i potężnych funkcji składni Pythona, umożliwiająca modyfikowanie zachowania funkcji lub klasy bez trwałej zmiany jej kodu. Są one szeroko stosowane w zagadnieniach przekrojowych, takich jak rejestrowanie czasów wykonywania, wymuszanie uwierzytelniania użytkowników, buforowanie wyników (zapamiętywanie) i kontrolowanie punktów końcowych API.

W skrócie dekorator to funkcja wyższego rzędu, która przyjmuje inną funkcję jako argument, definiuje zagnieżdżoną funkcję opakowującą, która wprowadza niestandardową logikę i zwraca tę funkcję opakowującą. Python zapewnia przejrzystą, skróconą składnię wykorzystującą symbol `@` do stosowania dekoratorów do funkcji, czyniąc je wizualnie eleganckimi.

Podczas pisania dekoratorów zazwyczaj chcesz, aby funkcja dekorowana obsługiwała dowolną liczbę argumentów pozycyjnych i słów kluczowych. Można to osiągnąć projektując zagnieżdżoną funkcję opakowującą tak, aby akceptowała `*args` i `**kwargs` i przekazywała je do funkcji docelowej. Dodatkowo powinieneś użyć `functools.wraps` w funkcji opakowującej, aby zachować dokumentację i metadane nazwy oryginalnej funkcji.

Dane wyjściowe kodu i wykonania

Ten skrypt demonstruje napisanie dekoratora rejestrowania i zawinięcie funkcji w celu sprawdzenia danych wejściowych, wyjściowych i metadanych.

decorators_tutorial.py
Spróbuj w Edytorze
from functools import wraps

# 1. Defining a decorator
def log_execution(func):
    @wraps(func) # Preserves original function metadata
    def wrapper(*args, **kwargs):
        print(f"[Log] Calling function: {func.__name__} with args: {args}")
        result = func(*args, **kwargs)
        print(f"[Log] Finished {func.__name__}. Result: {result}")
        return result
    return wrapper

# 2. Applying the decorator using @ syntax
@log_execution
def add_numbers(a, b):
    """Adds two numbers and returns the sum."""
    return a + b

# 3. Invoking the decorated function
sum_result = add_numbers(15, 25)
print("Sum Result:", sum_result)

# Verifying metadata preservation
print("Docstring preserved:", add_numbers.__doc__)
Wyjście terminala
[Log] Calling function: add_numbers with args: (15, 25)
[Log] Finished add_numbers. Result: 40
Sum Result: 40
Docstring preserved: Adds two numbers and returns the sum.

Wdrażanie krok po kroku

  • Zdefiniuj zewnętrzną funkcję dekoratora, która przyjmuje funkcję docelową jako argument.
  • Zaimportuj i zastosuj @functools.wraps(func) w zagnieżdżonej funkcji opakowania, aby chronić oryginalne metadane.
  • Zaimplementuj wrapper(*args, **kwargs), aby przechwytywać zmienne wejściowe, wstrzykiwać logikę i wywoływać funkcję docelową.
  • Zwróć obiekt funkcji opakowania z bloku nagłówka dekoratora.
  • Zastosuj dekorator, używając @decorator_name bezpośrednio nad sygnaturą funkcji docelowej.

Często zadawane pytania

Dlaczego @functools.wraps jest niezbędny wewnątrz dekoratorów?

Bez zawinięć dekorowana funkcja traci swoją pierwotną tożsamość (jej atrybuty __name__ i __doc__ są zastępowane atrybutami zagnieżdżonej funkcji opakowującej), co utrudnia debugowanie i introspekcję kodu.

Czy mogę zastosować wiele dekoratorów do jednej funkcji?

Tak! Możesz łączyć dekoratory w łańcuchy, umieszczając wiele @ linii. Python stosuje je od dołu do góry (najpierw wykonywana jest definicja funkcji najbliżej).

Powiązane tematy

Polecane zasoby Pythona

Poszerzaj swoją wiedzę dzięki powiązanym interaktywnym samouczkom, ściągawkom i porównaniom kodów.