我正在尝试找到创建类装饰器的最佳方法,该装饰器执行以下操作:
__init__
被调用目前,我只是保存对“原始”__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__
之后注入函数调用,并希望避免段错误。
答案 0 :(得分:6)
基于this post和this 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!!
基本理念是:
当您致电MyNewClass()
时,它会搜索元类,发现您已定义NewInitCaller
调用元类__call__
函数。
此功能使用MyNewClass
创建type
实例,
该实例运行自己的__init__
(打印“Init class”)。
元类然后调用实例的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!!