Припустимо, я написав декоратора, який робить щось дуже загальне. Наприклад, він може перетворити всі аргументи на певний тип, виконати журнал, здійснити запам'ятовування тощо.
Ось приклад:
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 - Включіть правильну підпис у docstring:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
Це погано через дублювання. Підпис все ще не відображатиметься належним чином у автоматично сформованій документації. Оновити функцію легко і забути про зміну докстрингу або зробити друк. [ І так, мені відомо про те, що docstring вже копіює тіло функції. Будь ласка, проігноруйте це; 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?
Будь-які інші підходи?
inspect.Signatureдодало до роботи з оформленими функціями.