【问题标题】:Decorating a function with a method (with arbitrary arguments)用方法装饰函数(带有任意参数)
【发布时间】:2013-01-25 23:53:09
【问题描述】:

参考文献

在写这个问题之前,我参考了以下一些有趣的问题,感觉这个场景没有解释/涵盖:

我有一个function,如下:

def simple_function():
    print 'this is a simple_function'

而且,我有以下class 和一些methods

class Test:

    def Test_method_1(self, func, args=None, kwargs=None):
        print 'this is Test::Test_method_1'
        <execute some instructions here>

    def Test_method_2(self):
        print 'this is Test::Test_method_2'

我使用Test_method_1simple_function如下:

t = Test()
t.Test_method_1(simple_function)

如果simple_function 接受任意参数,那么Test_method_1 的调用方式如下:

def simple_function(a, b, c, d=f, g=h):
    print 'this is a simple_function'

t = Test()
t.Test_method_1(simple_function, args=[a, b, c], kwargs={d:f, g:h})

现在,我想使用Test_method_2 作为decorator versionTest_method_1。所以simple_function的定义可以写成如下:

t = Test()

@t.Test_method_2
def simple_function():
    print 'this is a simple_function'

注意:Test_method_2 将使用适当的参数调用 Test_method_1

现在的问题:

  • 1 - 这可能吗?
  • 2 - 如果可能,如何将修饰函数的名称(此处为simple_function)作为参数传递给Test_method_2(包括self)?
  • 3 - [这是杀手问题,我相信] 我想将任意参数传递给 Test_method_2(装饰器)和 simple_function(被装饰的函数) - *args and **kwargs - @987654349 应该如何@被定义为接收参数(包括self)?

Test_method_2 用作 simple_function 的装饰器,两者都具有任意参数,如下所示:

t = Test()
@t.Test_method_2(a, b, c, d, e=f, g=h)
def simple_function(x, y, z, n=m):
    print 'this is a simple_function'

【问题讨论】:

  • 这就是flask@app.route('/path') 所做的事情,所以这是可能的,我对lxml 做了类似的事情,方法是装饰一个在Xpath 匹配时将被调用的函数,比如@ 987654357@
  • 你试过了吗?您为什么不尝试一下,看看它是否有效,然后询问哪些无效的问题?

标签: python function methods decorator python-decorators


【解决方案1】:

当然可以。 @decorator 语法所做的只是定义后面的函数,然后调用装饰器,传入后面的函数并用返回的任何内容替换对该函数的引用。

所以如下:

@foo
def bar():
    pass

翻译成:

def bar():
    pass
bar = foo(bar)

这意味着您的 t.Test_method_2() 方法必须期待一个参数,即要装饰的函数,并返回可调用的内容:

import functools

def Test_method_2(self, func):
    @functools.wraps(func)
    def wrapper(self, *args, **kw):
        print 'Wrapped function name:', func.__name__
        return func(*args, **kw)
    return wrapper

将是返回包装函数的最小装饰器,并在调用时打印包装函数的名称。 调用什么参数并不重要;我在这里使用了func,但它可以是任何合法的python标识符。

self 是方法签名的标准部分。因为您在实例 t 上引用 Test_method_2,所以 Python 会像处理所有方法一样自动为您处理 self 参数。

@ 之后的任何内容都只是一个表达式。所以如果你使用语法:

@t.Test_method_2(a, b, c, d, e=f, g=h)

那么Test_method_2() 应该返回一个装饰器函数。一层额外的范围嵌套应该可以做到这一点:

def Test_method_2(self, *args, **kw):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*wrapperargs, **wrapperkw):
            fargs = args + wrapperargs
            fkw = dict(kw)
            fkw.update(wrapperkw)
            return func(*fargs, **fkw)
        return wrapper
    return decorator

解构这个:

@t.Test_method_2(5, 6, 7, 8, baz='spam', ham='eggs')
def simple_function(x, y, z, n=m):
    print 'this is a simple_function'

@t.Test_method_2(5, 6, 7, 8, baz='spam', ham='eggs')之后的部分返回嵌套函数decorator

@decorator
def simple_function(x, y, z, n=m):
    print 'this is a simple_function'

然后python变成了:

simple_function = decorator(simple_function)

decorator(func) 返回wrapper(*wrapperargs, **wrapperkw)

调用simple_function(1, 2, foo='bar') 然后调用wrapper(1, 2, foo='bar') 调用原始 simple_function()fargs = [5, 6, 7, 8, 1, 2]fkw = {'baz': 'spam', 'ham': 'eggs', 'foo': 'bar'} 作为位置和关键字参数传入。

您在链接问题中看到的类装饰器模式以类似的方式工作; @ 之后的表达式返回被调用的内容;而不是一个嵌套函数,而是创建一个类实例。这只是装饰器使用的两种不同的存储状态的方法。

嵌套函数编写起来更紧凑,而使用类比嵌套作用域提供了更多的自省选项。

【讨论】:

    猜你喜欢
    • 2023-03-31
    • 2014-07-21
    • 2015-08-14
    • 2012-03-14
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多