简体   繁体   English

从另一个类继承的类装饰器

[英]Class Decorator when Inheriting from another class

Ive been on a tear of writing some decorators recently. 最近,我一直在写一些装饰工。 One of the ones I just wrote allows you to put the decorator just before a class definition, and it will cause every method of the class to print some logigng info when its run (more for debugging/initial super basic speed tests during a build) 我刚刚写的一个允许您将装饰器放在类定义之前,这将导致类的每个方法在运行时输出一些logigng信息(更多信息用于在构建过程中进行调试/初始超基本速度测试)

def class_logit(cls):

    class NCls(object):
        def __init__(self, *args, **kwargs):
            self.instance = cls(*args, **kwargs)

        @staticmethod
        def _class_logit(original_function):
            def arg_catch(*args, **kwargs):
                start = time.time()
                result = original_function(*args, **kwargs)

                print('Called: {0} | From: {1} | Args: {2} | Kwargs: {3} | Run Time: {4}'
                  ''.format(original_function.__name__, str(inspect.getmodule(original_function)),
                            args, kwargs, time.time() - start))
                return result

            return arg_catch

        def __getattribute__(self, s):
            try:
                x = super(NCls, self).__getattribute__(s)
            except AttributeError:
                pass
            else:
                return x
            x = self.instance.__getattribute__(s)
            if type(x) == type(self.__init__):
                return self._class_logit(x)
            else:
                return x

    return NCls

This works great when applied to a very basic class i create. 当将其应用于我创建的非常基本的类时,此方法效果很好。

Where I start to encounter issues is when I apply it to a class that is inheriting another - for instance, using QT: 我开始遇到问题的地方是将其应用于继承另一个类的类时(例如,使用QT):

@scld.class_logit
class TestWindow(QtGui.QDialog):
    def __init__(self):
        print self
        super(TestWindow, self).__init__()
a = TestWindow()

Im getting the following error... and im not entirely sure what to do about it! 我收到以下错误...并且我不完全确定该怎么做!

self.instance = cls(*args, **kwargs)
File "<string>", line 15, in __init__
TypeError: super(type, obj): obj must be an instance or subtype of type

Any help would be appreciated! 任何帮助,将不胜感激!

(Apologies in advance, no matter WHAT i do SO is breaking the formatting on my first bit of code... Im even manually spending 10 minutes adding spaces but its coming out incorrectly... sorry!) (预先道歉,无论我怎么做都破坏了我第一部分代码的格式...我什至手动花了10分钟添加空格,但它出来不正确...对不起!)

You are being a bit too intrusive with your decorator. 您对装饰器有点太过分了。

While if you want to profile methods defined on the Qt framework itself, a somewhat aggressive approach is needed, your decorator replaces the entire class by a proxy. 虽然如果要分析在Qt框架本身上定义的方法,则需要一种更具侵略性的方法,但装饰器将通过代理替换整个类。

Qt bindings are somewhat complicated indeed, and it is hard to tell why it is erroring when being instantiated in this case. Qt绑定确实有些复杂,在这种情况下实例化时很难说出它为什么出错。

So - first things first - if your intent would be to apply the decorator to a class hierarchy defined by yourself, or at least one defined in pure Python, a good approach there could be using metaclasses: with a metaclass you could decorate each method when a class is created, and do not mess anymore at runtime, when methods are retrieved from each class. 所以-首先-如果您的意图是将装饰器应用于您自己定义的类层次结构,或者至少将其应用于纯Python中定义的类层次结构,则可以使用元类作为一种好方法:使用元类,您可以在以下情况下装饰每个方法从每个类中检索方法时,将创建一个类,并且在运行时不会再混乱。

but Qt, as some other libraries, have its methods and classes defined in native code, and that will prevent you from wrapping existing methods in a new class. 但是Qt和其他一些库一样,在本机代码中定义了其方法和类,这将阻止您将现有方法包装在新类中。 So, wrapping the methods on attribute retrieval on __getattribute__ could work. 因此,将方法包装在__getattribute__上的属性检索上可以起作用。

Here is a simpler approach that instead of using a Proxy, just plug-in a foreign __getattribute__ that does the wrap-with-logger thing you want. 这是一种更简单的方法,而不是使用Proxy,而只需插入一个外部__getattribute__完成所需的包装程序。
Your mileage may vary with it. 您的里程可能会有所不同。 Specially, it won't be triggered if one method of the class is called by other method in native code - as this won't go through Python's attribute retrieval mechanism (instead, it will use C++ method retrieval directly). 特别是,如果该类的一个方法被本机代码中的其他方法调用,则不会触发该方法-因为该方法不会通过Python的属性检索机制进行处理(而是直接使用C ++方法检索)。

from  PyQt5 import QtWidgets, QtGui

def log_dec(func):
    def wraper(*args, **kwargs):
        print(func.__name__, args, kwargs)
        return func(*args, **kwargs)
    return wraper


def decorate(cls):
    def __getattribute__(self, attr):
        attr = super(cls, self).__getattribute__(attr)
        if callable(attr):
            return log_dec(attr)
        return attr
    cls.__getattribute__ = __getattribute__
    return cls

@decorate
class Example(QtGui.QWindow):
    pass

app = QtWidgets.QApplication([])

w = Example()
w.show()

(Of course, just replace the basic logger by your fancy logger above) (当然,只需用上面的精美记录器替换基本记录器)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM