Декодер python для отображения переданных и по умолчанию kwargs

Я новичок в python и декораторах, и я нахожусь в замешательстве в написании декоратора, который сообщает не только переданные args и kwargs, но также неизменные kwargs по умолчанию.

Это то, что у меня есть до сих пор.

def document_call(fn): def wrapper(*args, **kwargs): print 'function %s called with positional args %s and keyword args %s' % (fn.__name__, args, kwargs) return fn(*args, **kwargs) return wrapper @document_call def square(n, trial=True, output=False): # kwargs are a bit of nonsense to test function if not output: print 'no output' if trial: print n*n square(6) # with this call syntax, the default kwargs are not reported # function square called with positional args (6,) and keyword args {} # no output 36 square(7,output=True) # only if a kwarg is changed from default is it reported # function square called with positional args (7,) and keyword args {'output': True} 49 

«Проблема» заключается в том, что этот декоратор сообщает аргументы, которые передаются в вызове на квадрат, но не сообщают о значениях по умолчанию, определенных в квадратичном определении. Единственный способ сообщить kwargs – если они изменены с их значения по умолчанию, то есть переданы на квадратный вызов.

Любые рекомендации относительно того, как я получу кварты в квадратичном определении, также сообщают?

Отредактируйте после того, как вы выполните рекомендации по проверке, которые помогли мне в решении ниже. Я изменил вывод позиционных параметров, чтобы включить их имена, потому что я думал, что это облегчает понимание вывода.

 import inspect def document_call(fn): def wrapper(*args, **kwargs): argspec = inspect.getargspec(fn) n_postnl_args = len(argspec.args) - len(argspec.defaults) # get kwargs passed positionally passed = {k:v for k,v in zip(argspec.args[n_postnl_args:], args[n_postnl_args:])} # update with kwargs passed.update({k:v for k,v in kwargs.iteritems()}) print 'function %s called with \n positional args %s\n passed kwargs %s\n default kwargs %s' % ( fn.__name__, {k:v for k,v in zip(argspec.args, args[:n_postnl_args])}, passed, {k:v for k,v in zip(argspec.args[n_postnl_args:], argspec.defaults) if k not in passed}) return fn(*args, **kwargs) return wrapper 

Это был хороший опыт обучения. Оптимально видеть три разных решения одной и той же проблемы. Благодаря Ответчикам!

Вам нужно будет изучить функцию, которую вы завернули, чтобы прочитать значения по умолчанию. Вы можете сделать это с помощью функции inspect.getargspec() .

Функция возвращает кортеж, среди прочего, последовательность всех имен аргументов и последовательность значений по умолчанию. Последний из имен аргументов соединяется со значениями по умолчанию, чтобы сформировать пары по умолчанию; вы можете использовать это, чтобы создать словарь и извлечь из него неиспользуемые значения по умолчанию:

 import inspect argspec = inspect.getargspec(fn) positional_count = len(argspec.args) - len(argspec.defaults) defaults = dict(zip(argspec.args[positional_count:], argspec.defaults)) 

Вам нужно будет учесть, что позиционные аргументы также могут указывать аргументы по умолчанию, поэтому танец для определения аргументов ключевого слова немного больше связан, но выглядит так:

 def document_call(fn): argspec = inspect.getargspec(fn) positional_count = len(argspec.args) - len(argspec.defaults) defaults = dict(zip(argspec.args[positional_count:], argspec.defaults)) def wrapper(*args, **kwargs): used_kwargs = kwargs.copy() used_kwargs.update(zip(argspec.args[positional_count:], args[positional_count:])) print 'function %s called with positional args %s and keyword args %s' % ( fn.__name__, args[:positional_count], {k: used_kwargs.get(k, d) for k, d in defaults.items()}) return fn(*args, **kwargs) return wrapper 

Это определяет, какие ключевые фразы были фактически использованы как из переданных позиционных аргументов, так и из аргументов ключевого слова, а затем извлекает значения по умолчанию для тех, которые не используются.

Демо-версия:

 >>> square(39) function square called with positional args (39,) and keyword args {'trial': True, 'output': False} no output 1521 >>> square(39, False) function square called with positional args (39,) and keyword args {'trial': False, 'output': False} no output >>> square(39, False, True) function square called with positional args (39,) and keyword args {'trial': False, 'output': True} >>> square(39, False, output=True) function square called with positional args (39,) and keyword args {'trial': False, 'output': True} 

Поскольку wrapper функция декоратора принимает любой аргумент и просто передает все, конечно, он ничего не знает о параметрах обернутой функции и ее значениях по умолчанию.

Поэтому, не глядя на украшенную функцию, вы не получите эту информацию. К счастью, вы можете использовать модуль проверки для определения аргументов по умолчанию для завернутой функции.

Вы можете использовать функцию inspect.getargspec чтобы получить информацию о значениях аргументов по умолчанию в сигнатуре функции. Вам просто нужно правильно сопоставить их с именами параметров:

 def document_call(fn): argspec = inspect.getargspec(fn) defaultArguments = list(reversed(zip(reversed(argspec.args), reversed(argspec.defaults)))) def wrapper(*args, **kwargs): all_kwargs = kwargs.copy() for arg, value in defaultArguments: if arg not in kwargs: all_kwargs[arg] = value print 'function %s called with positional args %s and keyword args %s' % (fn.__name__, args, all_kwargs) # still make the call using kwargs, to let the function handle its default values return fn(*args, **kwargs) return wrapper 

Обратите внимание, что вы все равно можете улучшить это, так как прямо сейчас вы обрабатываете позиционные и именованные аргументы отдельно. Например, в вашей square функции вы также можете установить trial , передав ее как позиционный аргумент после n . Это заставит его не появляться в kwargs . Таким образом, вам нужно будет сопоставить позиционные аргументы с вашими kwargs, чтобы получить полную информацию. Вы можете получить всю информацию о позициях из argspec .