如何在Python 2.7中为包装函数添加关键字参数?

Sha*_*al5 6 decorator python-2.7 functools python-decorators

我首先要强调的是,我已经非常广泛地搜索了Web和Python文档+ StackOverflow,并且没有设法找到这个问题的答案.我还要感谢任何花时间阅读本文的人.

正如标题所示,我正在用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
Run Code Online (Sandbox Code Playgroud)

现在这个装饰器可以按以下方式使用:

@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()
Run Code Online (Sandbox Code Playgroud)

现在,虽然这适用于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没有发生的事情.

我很想得到你的帮助.感谢您抽出时间来阅读.

吉文

Ror*_*rke 5

如果您只将附加参数指定为关键字,则可以将它们从 kw 字典中取出(见下文)。如果您需要它们作为位置AND关键字参数,那么我认为您应该能够在原始函数上使用 inspect.getargspec,然后在 func_wrapper 中处理 args 和 kw。

下面的代码在 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()
Run Code Online (Sandbox Code Playgroud)

输出是

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
Run Code Online (Sandbox Code Playgroud)