Как я могу украсить функцию Python без изменения имен аргументов?

Следующий код Python определяет регистратор и функцию факториала, а затем вызывает функцию факториала с аргументом ключевого слова:

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))

в результате получается следующий результат (как и ожидалось): 120,

Теперь, если я раскомментирую декоратор логгера, я получу ошибку, потому что имя аргумента стало a вместо n:

Как я могу украсить функцию (например, факториал) без изменения имен аргументов?

1 ответ

Решение

Игнорируя синтаксис декоратора, вот что вы делаете:

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)

Таким образом, чтобы просто исправить это, используйте ту же декларацию, или не используйте ключевые аргументы (n=5)


Лучший способ исправить это - использовать распаковку во внутренней функции:

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_

Вот полезная статья на эту тему.

Другие вопросы по тегам