python:记录所有类的方法,而不装饰每个方法

Python: Logging all of a class' methods without decorating each one

我想记录一些类中的每个方法调用。我本来可以的

1
2
3
4
5
6
7
class Class1(object):
    @log
    def method1(self, *args):
        ...
    @log
    def method2(self, *args):
        ...

但是我在每节课上都有很多方法,我不想单独装饰每一个。目前,我尝试对元类使用hack(重写我记录的类'__getattribute__,这样如果我尝试获取一个方法,它将返回一个日志方法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LoggedMeta(type):
    def __new__(cls, name, bases, attrs):
        def __getattribute__(self, name_):
            attr = super().__getattribute__(name_)
            if isinstance(attr, (types.MethodType, types.FunctionType)) and not name_.startswith("__"):
                return makeLogged(attr) #This returns a method that first logs the method call, and then calls the original method.
            return attr
        attrs["__getattribute__"] = __getattribute__
    return type.__new__(cls, name, bases, attrs)

class Class1(object):
    __metaclass__ = LoggedMeta
    def method1(self, *args):
        ...

但是,我在python2.x上,super()语法不起作用。在我调用super的时候,我没有__getattribute__的类(但我有它的类名),所以我不能使用旧的super语法super(Class, Inst)

我之前试过使用元类,但是重写了所有的方法而不是__getattribute__,但是我还想记录静态方法调用,它们给了我一些麻烦。

我搜索了这类问题,但没有发现有人试图用这种方式改变课堂。

任何想法或帮助都将非常感谢。

编辑:我的解决方案是(大部分取自此线程):

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
import inspect, types

CLASS = 0
NORMAL = 1
STATIC = 2

class DecoratedMethod(object):

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

    def __get__(self, obj, cls=None):
        def wrapper(*args, **kwargs):
            print"before"
            if self.type == CLASS:
                #classmethods (unlike normal methods) reach this stage as bound methods, but args still contains the class
                #as a first argument, so we omit it.
                ret = self.func(*(args[1:]), **kwargs)
            else:
                ret = self.func(*args, **kwargs)
            print"after"
            return ret
        for attr in"__module__","__name__","__doc__":
            setattr(wrapper, attr, getattr(self.func, attr))
        if self.type == CLASS:
            return types.MethodType(wrapper, cls, type)
        elif self.type == NORMAL:
            return types.MethodType(wrapper, obj, cls)
        else:
            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, DecoratedMethod(meth, CLASS))
            else:
                # meth is a regular method
                setattr(cls, name, DecoratedMethod(meth, NORMAL))
        elif inspect.isfunction(meth):
            # meth is a staticmethod
            setattr(cls, name, DecoratedMethod(meth, STATIC))
    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"

后来我清理了一下,但这就是解决方案的本质。我需要类、静态方法和普通方法之间的区别,因为我希望

1
2
3
4
inst = MyClass()
assert type(inst.baz) == types.FunctionType
assert type(inst.foo) == types.MethodType
assert type(inst.bar) == types.MethodType


为什么不更改类对象?

您可以使用dir(MyClass)遍历类中的方法,并用包装版本替换它们…类似:

1
2
3
4
5
def logify(klass):
    for member in dir(klass):
        if not callable(getattr(klass, method))
            continue # skip attributes
        setattr(klass, method, log(method))

胡乱摆弄这样的东西…应该工作…


一个班级装饰师可以在这里帮忙。修饰整个类,并将日志记录功能添加到类具有的所有可调用属性中。


我建议从这篇文章中选择所有方法修饰器,那么您的代码将是

1
2
3
4
@for_all_methods(log)
class Class1():
   def method1(self): pass
   ...