Предположим, я написал декоратор, который делает что-то очень общее. Например, он может преобразовать все аргументы в определенный тип, выполнить ведение журнала, реализовать памятку и т. Д.
Вот пример:
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
Пока все хорошо. Однако есть одна проблема. Декорированная функция не сохраняет документацию оригинальной функции:
>>> help(funny_function)
Help on function g in module __main__:
g(*args, **kwargs)
К счастью, есть обходной путь:
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
На этот раз имя функции и документация верны:
>>> help(funny_function)
Help on function funny_function in module __main__:
funny_function(*args, **kwargs)
Computes x*y + 2*z
Но есть проблема: подпись функции неверна. Информация "* args, ** kwargs" практически бесполезна.
Что делать? Я могу вспомнить два простых, но ошибочных обходных пути:
1 - Включить правильную подпись в строку документации:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
Это плохо из-за дублирования. Подпись по-прежнему не будет правильно отображаться в автоматически сгенерированной документации. Легко обновить функцию и забыть об изменении строки документации или создании опечатки. [ И да, я знаю о том факте, что строка документации уже дублирует тело функции. Пожалуйста, игнорируйте это; funny_function - это случайный пример. ]
2 - Не использовать декоратор или использовать специальный декоратор для каждой конкретной подписи:
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
Это прекрасно работает для набора функций с одинаковой сигнатурой, но в целом бесполезно. Как я уже говорил в начале, я хочу иметь возможность использовать декораторы в целом.
Я ищу решение, которое является полностью общим и автоматическим.
Итак, вопрос: есть ли способ отредактировать подпись оформленной функции после ее создания?
В противном случае, можно ли написать декоратор, который извлекает сигнатуру функции и использует эту информацию вместо "* kwargs, ** kwargs" при создании декорированной функции? Как мне извлечь эту информацию? Как мне создать декорированную функцию - с помощью exec?
Есть ли другие подходы?