Una guida ai decoratori Python per programmatori avanzati

I decoratori Python sono uno strumento potente e flessibile per modificare il comportamento di funzioni o metodi. Consentono ai programmatori di estendere o modificare la funzionalità di oggetti richiamabili in modo pulito, leggibile e gestibile. Questo articolo esplora concetti avanzati relativi ai decoratori Python, tra cui decoratori annidati, argomenti dei decoratori e decoratori basati su classi.

Cosa sono i decoratori?

I decoratori sono funzioni che modificano il comportamento di un'altra funzione. Incapsulano un'altra funzione per estenderne il comportamento senza modificarne esplicitamente il codice. I decoratori sono definiti utilizzando la sintassi @decorator_name e sono posizionati sopra la definizione della funzione.

Sintassi di base del decoratore

Un decoratore semplice accetta una funzione come argomento, definisce una funzione interna che aggiunge un comportamento e quindi restituisce la funzione interna.

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

Funzioni decoratore con argomenti

I decoratori possono essere più flessibili accettando argomenti. Per creare un decoratore di questo tipo, devi scrivere una funzione che restituisca un decoratore. Ciò consente di aggiungere un comportamento più dinamico ai decoratori.

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

Decoratori di nidificazione

I decoratori possono essere annidati per combinare più comportamenti. Ad esempio, possiamo usare due o più decoratori su una singola funzione.

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

Decoratori basati sulle classi

In Python, i decoratori possono anche essere implementati come classi usando il metodo __call__. I decoratori basati su classi sono utili quando hai bisogno di una gestione dello stato e di un comportamento più complessi.

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

Utilizzo di functools.wraps per preservare i metadati

Quando si scrivono decoratori, la funzione decorata perde i suoi metadati originali, come il suo nome e la sua docstring. Il decoratore functools.wraps può essere usato per copiare i metadati della funzione originale nella funzione wrapper.

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.

Conclusione

I decoratori Python sono una potente funzionalità che consente una progettazione flessibile del codice e una modifica del comportamento. L'utilizzo avanzato, come i decoratori nidificati, i decoratori con argomenti e i decoratori basati su classi, può fornire ancora più funzionalità e leggibilità ai programmi Python. Comprendendo e utilizzando correttamente i decoratori, gli sviluppatori possono scrivere codice più conciso, efficiente e leggibile.