使用返回方法的类的装饰器?

时间:2017-01-30 18:15:00

标签: python decorator python-decorators

我目前有一个将函数包装到类中的装饰器。

(我们目前正在使用这个奇怪的自定义异步框架,其中每个异步调用被定义为具有大量样板代码的类。我的想法是只修饰函数然后返回适当的类。)

这个装饰器可以很好地处理类之外的函数。但是,在将它与方法一起使用时,self参数不再隐式传递,我不确定原因。

这是我可以放在一起的最好的例子

from __future__ import print_function

import functools


def test_wrap(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Args:", args)
        print("Kwargs:", kwargs)
        func(*args, **kwargs)
    return wrapper


def test_class_wrap(func):
    """Return a Command object for use with the custom framework we are using."""
    @functools.wraps(func, assigned=('__name__', '__module__'), updated=())
    class Command(object):
        def __init__(self, *args, **kwargs):
            print("Args:", args)
            print("Kwargs:", kwargs)
            func(*args, **kwargs)

    return Command


class MyObject(object):

    def __init__(self):
        self.value = 100

    @test_wrap
    def foo(self):
        print(self.value)

    @test_class_wrap
    def bar(self):
        print(self.value)


if __name__ == '__main__':
    obj = MyObject()
    obj.foo()

    print()
    obj.bar(obj)  # works
    # obj.bar()  # TypeError: bar() takes exactly 1 argument (0 given)
    # Why is self implicitly passed as an argument like with outher methods?

# Output
# Args: (<__main__.MyObject object at 0x7fe2bf9bb590>,)
# Kwargs: {}
# 100

# Args: (<__main__.MyObject object at 0x7fe2bf9bb590>,)
# Kwargs: {}
# 100

1 个答案:

答案 0 :(得分:1)

test_class_wrap什么都不做,只是返回一个类,因此__init__没有被调用。尝试使用传递args和kwargs的函数来包装类:

def test_class_wrap(func):
    """Return a Command object for use with the custom framework we are using."""
    @functools.wraps(func, assigned=('__name__', '__module__'), updated=())
    def wrapper(*args, **kwargs):
        class Command(object):
            def __init__(self, *args, **kwargs):
                print("Args:", args)
                print("Kwargs:", kwargs)
                func(*args, **kwargs)
        return Command(*args, **kwargs)
    return wrapper

...

if __name__ == '__main__':
    obj = MyObject()
    obj.foo()

    print()
    obj.bar()