关于python:编写一个类装饰器,将装饰器应用于所有方法

Writing a class decorator that applies a decorator to all methods

我正在尝试编写一个类修饰器,它将修饰器应用于所有类的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import inspect


def decorate_func(func):
    def wrapper(*args, **kwargs):
        print"before"
        ret = func(*args, **kwargs)
        print"after"
        return ret
    for attr in"__module__","__name__","__doc__":
        setattr(wrapper, attr, getattr(func, attr))
    return wrapper


def decorate_class(cls):
    for name, meth in inspect.getmembers(cls, inspect.ismethod):
        setattr(cls, name, decorate_func(meth))
    return cls


@decorate_class
class MyClass(object):

    def __init__(self):
        self.a = 10
        print"__init__"

    def foo(self):
        print self.a

    @staticmethod
    def baz():
        print"baz"

    @classmethod
    def bar(cls):
        print"bar"


obj = MyClass()
obj.foo()
obj.baz()
MyClass.baz()
obj.bar()
MyClass.bar()

它几乎可以工作,但@classmethod需要特殊处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ python test.py
before
__init__
after
before
10
after
baz
baz
before
Traceback (most recent call last):
  File"test.py", line 44, in <module>
    obj.bar()
  File"test.py", line 7, in wrapper
    ret = func(*args, **kwargs)
TypeError: bar() takes exactly 1 argument (2 given)

有没有办法很好地处理这个问题?我检查了@classmethod装饰方法,但我看不出有什么不同于其他"类型"方法的地方。

更新

这里是记录的完整解决方案(使用描述符很好地处理@staticmethods和@classmethods,以及AIX检测@classmethods与正常方法的技巧):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import inspect


class DecoratedMethod(object):

    def __init__(self, func):
        self.func = func

    def __get__(self, obj, cls=None):
        def wrapper(*args, **kwargs):
            print"before"
            ret = self.func(obj, *args, **kwargs)
            print"after"
            return ret
        for attr in"__module__","__name__","__doc__":
            setattr(wrapper, attr, getattr(self.func, attr))
        return wrapper


class DecoratedClassMethod(object):

    def __init__(self, func):
        self.func = func

    def __get__(self, obj, cls=None):
        def wrapper(*args, **kwargs):
            print"before"
            ret = self.func(*args, **kwargs)
            print"after"
            return ret
        for attr in"__module__","__name__","__doc__":
            setattr(wrapper, attr, getattr(self.func, attr))
        return wrapper


def decorate_class(cls):
    for name, meth in inspect.getmembers(cls):
        if inspect.ismethod(meth):
            if inspect.isclass(meth.im_self):
                # meth is a classmethod
                setattr(cls, name, DecoratedClassMethod(meth))
            else:
                # meth is a regular method
                setattr(cls, name, DecoratedMethod(meth))
        elif inspect.isfunction(meth):
            # meth is a staticmethod
            setattr(cls, name, DecoratedClassMethod(meth))
    return cls


@decorate_class
class MyClass(object):

    def __init__(self):
        self.a = 10
        print"__init__"

    def foo(self):
        print self.a

    @staticmethod
    def baz():
        print"baz"

    @classmethod
    def bar(cls):
        print"bar"


obj = MyClass()
obj.foo()
obj.baz()
MyClass.baz()
obj.bar()
MyClass.bar()


(P)EDOCX1(音译)0应该告诉你,字母名称1是一种古典方法:(p)字母名称


(P)(一个评论太长了)(p)(P)I took the liberty of adding the ability to specify which methods should get designated to your solution:(p)字母名称(P)USAGE:(p)字母名称


(P)The above answers do not apply directly to pyton3.Based on the other great answers I have been able to come up with the following solution:(p)字母名称(P)Now anytime a method in the tuple EDOCX1 English 2 is called,the Dirty Flag is set.of course,anything else can be put there too.它不需要将EDOCX1的英文本3分类包括在类别中,因为所有方法都需要被超越。然而,作为EDOCX1的一个英文字母4,使用了特殊的符号来表示阶级,我倾向于使一个阶级有意义。(p)