The following Python code defines a logger and a factorial function, then calls the factorial function with a keyword argument:
def logger(f):
def f_(a):
print("Call", a)
return f(a)
return f_
# @logger # uncomment this line to see the problem
def factorial(n):
return 1 if n == 0 else n * factorial(n-1)
print(factorial(n=5))
resulting in the following output (as expected): 120
.
Now if I uncomment the logger decorator, I get an error, because the name of the argument has become a
instead of n
:
How can I decorate a function (like factorial) without changing the names of the arguments?
Ignoring the decorator syntax, here's what you're doing:
def logger(f):
def f_(a):
print("Call", a)
return f(a)
return f_
def factorial(n):
return 1 if n == 0 else n * factorial(n-1)
# `factorial` is `def f_(a):` now
factorial = logger(factorial)
So to simply remedy this, use the same declaration, or don't use keyword arguments (n=5)
The better way to correct this is to use unpacking in your inner function:
import functools
def logger(f):
@functools.wraps(f)
def f_(*args, **kwargs):
print("Call", f.__name__, *args, *[f"{k}={v!r}" for k, v in kwargs.items()])
return f(*args, **kwargs)
return f_