2016-08-31 10 views
1

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

import inspect 
from functools import wraps 

# Base decorator 
def _process_arguments(func, *indices): 
    """ Apply the pre-processing function to each selected parameter """ 
    @wraps(func) 
    def wrap(f): 
     @wraps(f) 
     def wrapped_f(*args): 
      params = inspect.getargspec(f)[0] 

      args_out = list() 
      for ind, arg in enumerate(args): 
       if ind in indices: 
        args_out.append(func(arg)) 
       else: 
        args_out.append(arg) 

      return f(*args_out) 
     return wrapped_f 
    return wrap 


# Function that will be used to process each parameter 
def double(x): 
    return x * 2 

# Decorator called by end user 
def double_selected(*args): 
    return _process_arguments(double, *args) 

# End-user's function 
@double_selected(2, 0) 
def say_hello(a1, a2, a3): 
    """ doc string for say_hello """ 
    print('{} {} {}'.format(a1, a2, a3)) 

say_hello('say', 'hello', 'arguments') 

Результат этого кода должен быть и:

saysay hello argumentsarguments 

Однако запуск справки по say_hello дает мне:

say_hello(*args, **kwargs) 
    doc string for say_hello 

Все сохраняется, за исключением имен параметров.

Кажется, мне просто нужно добавить еще один @wraps() где-нибудь, но где?

+0

вы используете Python 2 или Python 3? – bravosierra99

ответ

0

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

comments on Bruce Eckel's post на декораторах с аргументами дал мне самые большие намеки на достижение моего желаемого результата.

Ключ в том, чтобы удалить среднюю функцию из моей функции _process_arguments и поместить ее параметр в следующую, вложенную функцию. Это отчасти имеет смысл для меня сейчас ... но это работает:

import inspect 
from decorator import decorator 

# Base decorator 
def _process_arguments(func, *indices): 
    """ Apply the pre-processing function to each selected parameter """ 
    @decorator 
    def wrapped_f(f, *args): 
     params = inspect.getargspec(f)[0] 

     args_out = list() 
     for ind, arg in enumerate(args): 
      if ind in indices: 
       args_out.append(func(arg)) 
      else: 
       args_out.append(arg) 

     return f(*args_out) 
    return wrapped_f 


# Function that will be used to process each parameter 
def double(x): 
    return x * 2 

# Decorator called by end user 
def double_selected(*args): 
    return _process_arguments(double, *args) 

# End-user's function 
@double_selected(2, 0) 
def say_hello(a1, a2,a3): 
    """ doc string for say_hello """ 
    print('{} {} {}'.format(a1, a2, a3)) 

say_hello('say', 'hello', 'arguments') 
print(help(say_hello)) 

И результат:

saysay hello argumentsarguments 
Help on function say_hello in module __main__: 

say_hello(a1, a2, a3) 
    doc string for say_hello 
0

Я экспериментировал с этим:

>>> from functools import wraps 
>>> def x(): print(1) 
... 
>>> @wraps(x) 
... def xyz(a,b,c): return x 


>>> xyz.__name__ 
'x' 
>>> help(xyz) 
Help on function x in module __main__: 

x(a, b, c) 

AFAIK, это не имеет ничего общего с wraps сам по себе, но вопрос, связанный с help. Действительно, поскольку help проверяет ваши объекты, чтобы предоставить информацию, в том числе __doc__ и другие атрибуты, вот почему вы получаете это поведение, хотя ваша обернутая функция имеет другой список аргументов. Хотя, wraps не обновляется, что автоматически (список аргументов), что он действительно обновляет этот кортеж и __dict__ который технически объекты пространства имен:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', 
         '__annotations__') 
WRAPPER_UPDATES = ('__dict__',) 

Если вы не уверены о том, как wraps работы, вероятно, это поможет, если вы прочитаете исходный код из стандартной библиотеки: functools.py.

Кажется, мне просто нужно добавить еще один @wraps() где-нибудь, а где?

Нет, вам не нужно добавить еще один wraps в вашем коде, help как я уже говорил выше, работает таким образом, проверяя свои объекты. Аргументы функции связаны с объектами кода (__code__), потому что аргументы вашей функции хранятся/представлены в этом объекте, wraps не имеет возможности обновить аргумент обертки, как обернутую функцию (продолжение с приведенным выше примером):

>>> xyz.__code__.co_varnames 

>>> xyz.__code__.co_varnames = x.__code__.co_varnames 
AttributeError: readonly attribute 

Если help отображается, что функция xyz имеет этот список аргументов () вместо (a, b, c) то это явно не так! И то же самое относится к wraps, чтобы изменить список аргументов обертки для обернутого, было бы громоздким! Так что это не должно быть проблемой.

>>> @wraps(x, ("__code__",)) 
... def xyz(a,b,c): pass 
... 

>>> help(xyz) 
Help on function xyz in module __main__: 

xyz() 

Но xyz() возвращается x():

>>> xyz() 
1 

Для ссылки на другие взглянуть на этот вопрос или Python Документация

What does functools.wraps do?

 Смежные вопросы

  • Нет связанных вопросов^_^