Вот более новый метод решения этой проблемы с использованием inspect.signature
(для Python 3.3+). Я приведу пример, который можно сначала запустить/протестировать самостоятельно, а затем показать, как модифицировать исходный код с его помощью.
Вот тестовая функция, которая просто суммирует переданные ей аргументы/kwargs; требуется по крайней мере один аргумент (a
) и есть один аргумент, содержащий только ключевое слово, со значением по умолчанию (b
), просто для проверки различных аспектов сигнатур функций.
def silly_sum(a, *args, b=1, **kwargs):
return a + b + sum(args) + sum(kwargs.values())
Теперь давайте создадим обертку для silly_sum
, которую можно вызывать так же, как silly_sum
(с исключением, о котором мы еще поговорим), но которая передает только kwargs обернутой silly_sum
.
def wrapper(f):
sig = inspect.signature(f)
def wrapped(*args, **kwargs):
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
print(bound_args) # just for testing
all_kwargs = bound_args.arguments
assert len(all_kwargs.pop("args", [])) == 0
all_kwargs.update(all_kwargs.pop("kwargs"))
return f(**all_kwargs)
return wrapped
sig.bind
возвращает объект BoundArguments
, но при этом не учитываются значения по умолчанию, если вы не вызываете apply_defaults
явно. Это также создаст пустой кортеж для args и пустой dict для kwargs, если *args
/**kwargs
не были заданы.
sum_wrapped = wrapper(silly_sum)
sum_wrapped(1, c=9, d=11)
# prints <BoundArguments (a=1, args=(), b=1, kwargs={'c': 9, 'd': 11})>
# returns 22
Затем мы просто получаем словарь аргументов и добавляем любые **kwargs
. Исключением для использования этой оболочки является то, что *args
нельзя передать в функцию. Это потому, что для них нет имен, поэтому мы не можем преобразовать их в kwargs. Если передача их как kwarg с именем args допустима, это можно было бы сделать вместо этого.
Вот как это можно применить к исходному коду:
import inspect
class mydec(object):
def __init__(self, f, *args, **kwargs):
self.f = f
self._f_sig = inspect.signature(f)
def __call__(self, *args, **kwargs):
bound_args = self._f_sig.bind(*args, **kwargs)
bound_args.apply_defaults()
all_kwargs = bound_args.arguments
assert len(all_kwargs.pop("args"), []) == 0
all_kwargs.update(all_kwargs.pop("kwargs"))
hozer(**all_kwargs)
self.f(*args, **kwargs)
person
Nathan
schedule
04.04.2019