Angenommen, ich habe einen Dekorateur geschrieben, der etwas sehr Allgemeines tut. Beispielsweise können alle Argumente in einen bestimmten Typ konvertiert, eine Protokollierung durchgeführt, eine Memoisierung implementiert usw. werden.
Hier ist ein Beispiel:
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
Alles gut soweit. Es gibt jedoch ein Problem. Die dekorierte Funktion behält nicht die Dokumentation der ursprünglichen Funktion bei:
>>> help(funny_function)
Help on function g in module __main__:
g(*args, **kwargs)
Zum Glück gibt es eine Problemumgehung:
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
Diesmal sind der Funktionsname und die Dokumentation korrekt:
>>> help(funny_function)
Help on function funny_function in module __main__:
funny_function(*args, **kwargs)
Computes x*y + 2*z
Es gibt aber immer noch ein Problem: Die Funktionssignatur ist falsch. Die Information "* args, ** kwargs" ist so gut wie nutzlos.
Was ist zu tun? Ich kann mir zwei einfache, aber fehlerhafte Problemumgehungen vorstellen:
1 - Fügen Sie die richtige Signatur in die Dokumentzeichenfolge ein:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
Dies ist wegen der Duplizierung schlecht. Die Signatur wird in der automatisch generierten Dokumentation immer noch nicht richtig angezeigt. Es ist einfach, die Funktion zu aktualisieren und das Ändern der Dokumentzeichenfolge zu vergessen oder einen Tippfehler zu machen. [ Und ja, mir ist bewusst, dass der Docstring den Funktionskörper bereits dupliziert. Bitte ignorieren Sie dies; lustige_Funktion ist nur ein zufälliges Beispiel.]]
2 - Verwenden Sie für jede Unterschrift keinen Dekorateur oder einen Spezialdekorateur:
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
Dies funktioniert gut für eine Reihe von Funktionen mit identischer Signatur, ist jedoch im Allgemeinen nutzlos. Wie eingangs gesagt, möchte ich Dekorateure ganz generisch einsetzen können.
Ich suche nach einer Lösung, die vollständig allgemein und automatisch ist.
Die Frage ist also: Gibt es eine Möglichkeit, die dekorierte Funktionssignatur zu bearbeiten, nachdem sie erstellt wurde?
Kann ich andernfalls einen Dekorator schreiben, der die Funktionssignatur extrahiert und diese Informationen anstelle von "* kwargs, ** kwargs" verwendet, wenn die dekorierte Funktion erstellt wird? Wie extrahiere ich diese Informationen? Wie soll ich die dekorierte Funktion konstruieren - mit exec?
Irgendwelche anderen Ansätze?
inspect.Signature
zum Umgang mit dekorierten Funktionen beigetragen hat.