简体   繁体   中英

How can I decorate a Python function without changing the names of the arguments?

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_

Here's a useful article on the topic.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM