2016-12-05 2 views
4

Прежде всего хочу подчеркнуть, что я обыскал как в Интернете, так и в документации на Python + StackOverflow, особенно очень подробно, и мне не удалось найти ответ на этот вопрос. Я также хочу поблагодарить любого, кто тратит время, чтобы прочитать это.Как добавить аргументы ключевого слова к завернутой функции в Python 2.7?

Как видно из названия, я пишу декоратора в Python, и я хочу, чтобы добавить ключевые аргументы в обернутой функции (обратите внимание: Я знаю, как добавить аргументы самого декоратора, это не то, что я спрашиваю).

Настоящий рабочий код фрагмента кода, который я написал, который делает именно это для Python 3 (в частности, Python 3.5). Он использует аргументы декоратора, добавляет аргументы ключевого слова к завершенной функции, а также определяет и добавляет новую функцию к завернутой функции.

from functools import wraps 

def my_decorator(decorator_arg1=None, decorator_arg2=False): 
    # Inside the wrapper maker 

    def _decorator(func): 
     # Do Something 1 

     @wraps(func) 
     def func_wrapper(
       *args, 
       new_arg1=False, 
       new_arg2=None, 
       **kwds): 
      # Inside the wrapping function 
      # Calling the wrapped function 
      if new_arg1: 
       return func(*args, **kwds) 
      else: 
       # do something with new_arg2 
       return func(*args, **kwds) 

     def added_function(): 
      print("Do Something 2") 

     func_wrapper.added_function = added_function 
     return func_wrapper 

    return _decorator 

Теперь этот декоратор можно использовать следующим образом:

@my_decorator(decorator_arg1=4, decorator_arg2=True) 
def foo(a, b): 
    print("a={}, b={}".format(a,b)) 

def bar(): 
    foo(a=1, b=2, new_arg1=True, new_arg2=7) 
    foo.added_function() 

Теперь, в то время как это работает на Python 3.5 (и я предполагаю, что для любого 3.x), мне не удалось сделать он работает для Python 2.7. Я получаю SyntaxError: invalid syntax в первой строке, которая пытается определить новый аргумент ключевого слова для func_wrapper, что означает строку, указывающую new_arg1=False,, при импорте модуля, содержащего этот код.

Перемещение новых ключевых слов в начало списка аргументов func_wrapper решает SyntaxError, но, похоже, винт с подпиткой завернутой функции; Я получаю сообщение об ошибке TypeError: foo() takes exactly 2 arguments (0 given) при звонке foo(1, 2). Эта ошибка исчезает, если я однозначно назначаю аргументы, как в foo(a=1, b=2), но этого явно недостаточно - неудивительно, что мои новые аргументы ключевых слов, похоже, «крадут» первые два позиционных аргумента, отправленные в завернутую функцию. Этого не произошло с Python 3.

Я бы с удовольствием помог вам в этом. Спасибо, что нашли время, чтобы прочитать это.

Шей

+1

код не работает, как есть: вы должны импортировать functools, и есть заявление (возможно, печать ("added_function")) в added_function. –

+0

@RoryYorke Отредактировано! Спасибо, человек! :) – ShayPal5

+1

Как смешно я задавал именно этот вопрос в одно и то же время (ну через 20 минут после вас). Хорошо, что я снова просмотрел «Вопросы, которые могут уже иметь ваш ответ» после того, как я закончил просить, и до того, как я нажал кнопку «отправить свой вопрос». – Davide

ответ

3

Если вы только когда-либо указать дополнительные аргументы в качестве ключевых слов, вы можете получить их из словаря кВт (см. Ниже) Если вам нужны они в качестве позиционных И аргументов ключевого слова, то я думаю, что вы должны иметь возможность использовать inspect.getargspec в исходной функции, а затем обрабатывать args и kw в func_wrapper.

Код ниже проверен на Ubuntu 14.04 с Python 2.7, 3.4 (оба варианта Ubuntu) и 3.5 (из Continuum).

from functools import wraps 

def my_decorator(decorator_arg1=None, decorator_arg2=False): 
    # Inside the wrapper maker 

    def _decorator(func): 
     # Do Something 1 
     @wraps(func) 
     def func_wrapper(
       *args, 
       **kwds): 
      # new_arg1, new_arg2 *CANNOT* be positional args with this technique 
      new_arg1 = kwds.pop('new_arg1',False) 
      new_arg2 = kwds.pop('new_arg2',None) 
      # Inside the wrapping function 
      # Calling the wrapped function 
      if new_arg1: 
       print("new_arg1 True branch; new_arg2 is {}".format(new_arg2)) 
       return func(*args, **kwds) 
      else: 
       print("new_arg1 False branch; new_arg2 is {}".format(new_arg2)) 
       # do something with new_arg2 
       return func(*args, **kwds) 

     def added_function(): 
      # Do Something 2 
      print('added_function') 

     func_wrapper.added_function = added_function 
     return func_wrapper 

    return _decorator 

@my_decorator(decorator_arg1=4, decorator_arg2=True) 
def foo(a, b): 
    print("a={}, b={}".format(a,b)) 

def bar(): 
    pass 
    #foo(1,2,True,7) # won't work 
    foo(1, 2, new_arg1=True, new_arg2=7) 
    foo(a=3, b=4, new_arg1=False, new_arg2=42) 
    foo(new_arg2=-1,b=100,a='AAA') 
    foo(b=100,new_arg1=True,a='AAA') 
    foo.added_function() 

if __name__=='__main__': 
    import sys 
    sys.stdout.flush() 
    bar() 

Выход

new_arg1 True branch; new_arg2 is 7 
a=1, b=2 
new_arg1 False branch; new_arg2 is 42 
a=3, b=4 
new_arg1 False branch; new_arg2 is -1 
a=AAA, b=100 
new_arg1 True branch; new_arg2 is None 
a=AAA, b=100 
added_function 
+0

Это имеет смысл, спасибо!Единственная проблема, с которой я столкнулся, заключается в том, что новые аргументы ключевых слов являются гражданами второго сорта; вы должны прочитать код, чтобы знать, что они есть, и что они являются аргументами, которые будут использоваться в вызове функции. В примере кода Python 3 их статус как аргументов ключевого слова более читабельен и ясен из кода. Это, конечно, проблема с Python 2, а не с вашим ответом. : P – ShayPal5

+0

Btw, нет - я ** не ** нуждаюсь в них в качестве позиционных аргументов. :) – ShayPal5

+0

Добавленные аргументы не будут работать как позиционные аргументы в Python 3: '* args' будет захватывать все позиционные аргументы, а введенный вами синтаксис был введен для добавления аргументов только для ключевого слова: https://www.python.org/dev/peps/pep-3102/ –