Сохранение подписей декорированных функций

Предположим, я написал декоратор, который делает что-то очень общее. Например, он может преобразовать все аргументы в определенный тип, выполнить ведение журнала, реализовать памятку и т. Д.

Вот пример:

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?

Любые другие подходы?

Ответы на вопрос(6)

Ваш ответ на вопрос