Supponiamo che io abbia scritto un decoratore che fa qualcosa di molto generico. Ad esempio, potrebbe convertire tutti gli argomenti in un tipo specifico, eseguire la registrazione, implementare la memorizzazione, ecc.
Ecco un esempio:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
>>> funny_function("3", 4.0, z="5")
22
Finora tutto bene. Tuttavia, c'è un problema. La funzione decorata non conserva la documentazione della funzione originale:
>>> help(funny_function)
Help on function g in module __main__:
g(*args, **kwargs)
Fortunatamente, c'è una soluzione alternativa:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
Questa volta, il nome della funzione e la documentazione sono corretti:
>>> help(funny_function)
Help on function funny_function in module __main__:
funny_function(*args, **kwargs)
Computes x*y + 2*z
Ma c'è ancora un problema: la firma della funzione è sbagliata. L'informazione "* args, ** kwargs" è quasi inutile.
Cosa fare? Posso pensare a due soluzioni alternative semplici ma imperfette:
1 - Includere la firma corretta nella docstring:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
Questo è negativo a causa della duplicazione. La firma non verrà comunque mostrata correttamente nella documentazione generata automaticamente. È facile aggiornare la funzione e dimenticare di modificare la docstring o di fare un errore di battitura. [ E sì, sono consapevole del fatto che la docstring duplica già il corpo della funzione. Si prega di ignorarlo; funny_function è solo un esempio casuale. ]
2 - Non utilizzare un decoratore o un decoratore speciale per ogni firma specifica:
def funny_functions_decorator(f):
def g(x, y, z=3):
return f(int(x), int(y), z=int(z))
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
Funziona bene per un insieme di funzioni che hanno una firma identica, ma in generale è inutile. Come ho detto all'inizio, voglio essere in grado di utilizzare i decoratori in modo del tutto generico.
Sto cercando una soluzione che sia completamente generale e automatica.
Quindi la domanda è: c'è un modo per modificare la firma della funzione decorata dopo che è stata creata?
Altrimenti, posso scrivere un decoratore che estrae la firma della funzione e utilizza quell'informazione invece di "* kwargs, ** kwargs" quando costruisce la funzione decorata? Come estraggo queste informazioni? Come dovrei costruire la funzione decorata - con exec?
Qualche altro approccio?
inspect.Signature
aggiungere alla gestione delle funzioni decorate.