Wenn Sie einen Decorator verwenden, ersetzen Sie eine Funktion durch eine andere. Mit anderen Worten, wenn Sie einen Dekorator haben
def logged(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
Wenn Sie dann sagen
@logged
def f(x):
"""does some math"""
return x + x * x
ist es genau dasselbe, als wenn man sagt
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
und Ihre Funktion f
wird ersetzt durch die Funktion with_logging
. Leider bedeutet dies, dass, wenn Sie dann sagen
print(f.__name__)
es wird gedruckt with_logging
weil das der Name Ihrer neuen Funktion ist. In der Tat, wenn Sie sich den Docstring für f
wird sie leer sein, weil with_logging
hat keinen Docstring, so dass der Docstring, den Sie geschrieben haben, nicht mehr vorhanden sein wird. Wenn Sie sich das pydoc-Ergebnis für diese Funktion ansehen, wird sie auch nicht als Funktion mit einem Argument aufgeführt x
; stattdessen wird es als Einnahme von *args
und **kwargs
denn das ist es, was with_logging braucht.
Wenn die Verwendung eines Dekorators immer bedeuten würde, dass diese Information über eine Funktion verloren geht, wäre das ein ernstes Problem. Das ist der Grund, warum wir functools.wraps
. Dies nimmt eine Funktion, die in einem Dekorator verwendet wird und fügt die Funktionalität des Kopierens des Funktionsnamens, des docstring, der Argumentenliste, etc. hinzu. Und da wraps
selbst ein Dekorator ist, tut der folgende Code das Richtige:
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print(f.__name__) # prints 'f'
print(f.__doc__) # prints 'does some math'