Come creare e utilizzare i decoratori in Python

Scopri come scrivere e applicare decoratori in Python. Padroneggiare le funzioni di wrapper, modificare il comportamento, gestire gli argomenti e preservare i metadati con i wrapper.

Prova questa soluzione nell'editor

Panoramica

I decoratori sono una delle funzionalità di sintassi più eleganti e potenti di Python, consentendo di modificare il comportamento di una funzione o di una classe senza modificarne permanentemente il codice. Sono ampiamente utilizzati per questioni trasversali come la registrazione dei tempi di esecuzione, l'applicazione dell'autenticazione dell'utente, la memorizzazione nella cache dei risultati (memoizzazione) e il controllo degli endpoint API.

Dietro le quinte, un decoratore è una funzione di ordine superiore che accetta un'altra funzione come argomento, definisce una funzione wrapper nidificata che inserisce una logica personalizzata e restituisce questa funzione wrapper. Python fornisce una sintassi abbreviata pulita utilizzando il simbolo `@` per applicare decoratori alle funzioni, rendendole visivamente eleganti.

Quando si scrivono decoratori, in genere si desidera che la funzione decorata supporti un numero qualsiasi di argomenti posizionali e di parole chiave. Puoi ottenere questo risultato progettando la funzione wrapper nidificata in modo che accetti "*args" e "**kwargs" e li passi alla funzione di destinazione. Inoltre, dovresti utilizzare `functools.wraps` sulla funzione wrapper per preservare la docstring della funzione originale e i metadati del nome.

Codice e output di esecuzione

Questo script dimostra la scrittura di un decoratore di registrazione e il confezionamento di una funzione per controllare input, output e metadati.

decorators_tutorial.py
Prova nell'editor
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__)
Uscita terminale
[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.

Implementazione passo dopo passo

  • Definire una funzione di decorazione esterna che accetta la funzione di destinazione come argomento.
  • Importa e applica @functools.wraps(func) sulla funzione wrapper nidificata per proteggere i metadati originali.
  • Implementa wrapper(*args, **kwargs) per acquisire variabili di input, inserire logica e richiamare la funzione di destinazione.
  • Restituisce l'oggetto funzione wrapper dal blocco di intestazione del decoratore.
  • Applica il decoratore utilizzando @decorator_name direttamente sopra la firma della funzione di destinazione.

Domande frequenti

Perché @functools.wraps è necessario all'interno dei decoratori?

Senza wrap, la funzione decorata perde la sua identità originale (i suoi attributi __name__ e __doc__ vengono sostituiti da quelli della funzione wrapper nidificata), il che rende difficile il debug e l'introspezione del codice.

Posso applicare più decoratori a una singola funzione?

Sì! Puoi concatenare i decoratori posizionando in pila più righe @. Python li applica dal basso verso l'alto (il più vicino alla definizione della funzione viene eseguito per primo).

Argomenti correlati

Risorse Python consigliate

Espandi le tue conoscenze con tutorial interattivi, foglietti illustrativi e confronti di codici correlati.