5

一部のクラスですべてのメソッド呼び出しをログに記録したい。私はすることができた

class Class1(object):
    @log
    def method1(self, *args):
        ...
    @log
    def method2(self, *args):
        ...

しかし、すべてのクラスに多くのメソッドがあり、すべてを個別に装飾したくありません。現在、メタクラスでハックを使用してみました (ログに記録されたクラスをオーバーライド__getattribute__して、メソッドを取得しようとすると、代わりにログ メソッドが返されるようにします)。

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):
        ...

ただし、私は Python 2.X を使用しており、super() 構文は機能しません。super を呼び出した時点では、__getattribute__のクラスがありません (ただし、そのクラス名はあります)。そのため、古い super 構文は使用できませんsuper(Class, Inst)

以前にメタクラスを使用しようとしましたが、代わりにすべてのメソッドをオーバーライドしましたが__getattribute__、静的メソッド呼び出しもログに記録したいので、問題が発生しました。

この種の質問を検索しましたが、この方法でクラスを変更しようとした人は見つかりませんでした。

どんなアイデアや助けも大歓迎です。

編集:私の解決策はこれでした(主にこのスレッドから取られました):

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"

後で少しクリーンアップしましたが、それがソリューションの本質です。したいので、クラス、静的、および通常のメソッドの間にこの違いが必要です

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

4 に答える 4

10

クラスオブジェクトを変更してみませんか?

クラス内のメソッドをdir(MyClass)調べて、それらをラップされたバージョンに置き換えることができます...次のようなもの:

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

このようなものをいじってください...うまくいくはずです...

于 2013-06-06T12:25:13.163 に答える
0

この SO postから for_all_methods デコレータを取得することをお勧めします。その後、コードは次のようになります

@for_all_methods(log)
class Class1():
   def method1(self): pass
   ...
于 2016-08-25T13:38:20.453 に答える