使用装饰器在__init__之后注入函数调用

时间:2013-04-15 14:10:33

标签: python pyqt4 decorator

我正在尝试找到创建类装饰器的最佳方法,该装饰器执行以下操作:

  1. 将一些函数注入装饰类
  2. 在装饰类“__init__被调用
  3. 之后,强制调用其中一个函数

    目前,我只是保存对“原始”__init__方法的引用,并将其替换为调用原始函数和我的附加函数的__init__。它看起来与此相似:

    orig_init = cls.__init__
    
    def new_init(self, *args, **kwargs):
        """
        'Extend' wrapped class' __init__ so we can attach to all signals
        automatically
        """
    
        orig_init(self, *args, **kwargs)
        self._debugSignals()
    
    cls.__init__ = new_init
    

    有没有更好的方法来“扩充”原始__init__或在其他地方注入我的电话?我真正需要的只是在创建对象后的某个时间调用self._debugSignals()。我也希望它自动发生,这就是为什么我认为__init__之后是一个好地方。

    额外的错误。装饰说明

    值得一提的是这个装饰器的一些背景知识。您可以找到完整代码here。装饰器的要点是自动附加到任何PyQt信号并在它们被发射时打印。当我装饰我自己的QtCore.QObject子类时装饰器工作正常,但我最近一直试图automatically decorate all QObject children

    我希望在应用程序中有一个'调试'模式,我可以自动打印所有信号,以确保事情按照我的预期进行。我相信这会导致TONS的调试,但我仍然希望看到发生了什么。

    问题是我当前版本的装饰器导致segfault when replacing QtCore.QObject.__init__。我试过调试这个,但是代码都是SIP生成的,我没有多少经验。

    所以,我想知道是否有更安全,更pythonic的方式在__init__之后注入函数调用,并希望避免段错误。

2 个答案:

答案 0 :(得分:6)

基于this postthis answer,另一种方法是通过自定义metaclass。这将如下工作(在Python 2.7中测试):

# define a new metaclass which overrides the "__call__" function
class NewInitCaller(type):
    def __call__(cls, *args, **kwargs):
        """Called when you call MyNewClass() """
        obj = type.__call__(cls, *args, **kwargs)
        obj.new_init()
        return obj


# then create a new class with the __metaclass__ set as our custom metaclass
class MyNewClass(object):
    __metaclass__ = NewInitCaller
    def __init__(self):
        print "Init class"
    def new_init(self):
        print "New init!!"

# when you create an instance
a = MyNewClass()
>>> Init class
>>> New init!!

基本理念是:

  1. 当您致电MyNewClass()时,它会搜索元类,发现您已定义NewInitCaller

  2. 调用元类__call__函数。

  3. 此功能使用MyNewClass创建type实例,

  4. 该实例运行自己的__init__(打印“Init class”)。

  5. 元类然后调用实例的new_init函数。

答案 1 :(得分:0)

以下是基于this post's accepted answer的Python 3.x解决方案。另请参阅PEP 3115以供参考,我认为理由是一个有趣的读物。

以上示例中的更改随评论一起显示;唯一真正的变化是定义元类的方式,所有其他都是微不足道的2to3修改。

# define a new metaclass which overrides the "__call__" function
class NewInitCaller(type):
    def __call__(cls, *args, **kwargs):
        """Called when you call MyNewClass() """
        obj = type.__call__(cls, *args, **kwargs)
        obj.new_init()
        return obj

# then create a new class with the metaclass passed as an argument
class MyNewClass(object, metaclass=NewInitCaller):  # added argument
    # __metaclass__ = NewInitCaller  this line is removed; would not have effect
    def __init__(self):
        print("Init class")  # function, not command
    def new_init(self):
        print("New init!!")  # function, not command

# when you create an instance
a = MyNewClass()
>>> Init class
>>> New init!!