考虑以下装饰者:
class connector(object):
def __init__(self, signal):
self.signal = signal
def __call__(self, slot_func):
def wrapper(*args, **kwargs):
slot_func(*args, **kwargs)
self.signal.connect(wrapper)
以下信号,以及我需要装饰的方法类:
from signalslot import Signal
update = Signal()
class manager(object):
# SOME CODE CUT
@connector(update)
def update(self):
print("I'm updating, yay!!!!")
正如你所看到的,我需要向装饰器传递一些额外的参数,在这种情况下 - 我需要连接到的信号。 如何通过自我?
我问的原因是,因为如果我尝试将这个装饰器应用于方法而不是函数,它会失败并出现以下错误:
TypeError:update()缺少1个必需的位置参数:'self'
更具体地说,如果我尝试发出信号:
update.emit()
是的,我在该项目中使用"signalslot"。
答案 0 :(得分:2)
self
必须是位置参数,而不是关键字参数:
def wrapper(self, *args, **kwargs):
# ^^^^ You can't use "self=None" here.
slot_func(self, *args, **kwargs)
如果您需要区分功能和方法,请改为使用descriptor。
但是,如果您尝试连接信号,则需要在每个实例上绑定方法。您最好在实例创建时连接信号:
class manager(object):
def __init__(self):
update.connect(self.update)
def update(self):
print("I'm updating, yay!!!!")
调用manager.__init__
时,您有一个新实例,然后您可以创建一个 self.update
绑定方法来接收信号。
你仍然可以使用装饰器,但你最好在课堂上注册 哪些功能可以作为信号处理程序;您必须在实例创建时枚举类中的所有函数,然后绑定所有这些信号:
class connector(object):
def __init__(self, signal):
self.signal = signal
def __call__(self, slot_func):
slot_func._signal_handler = self.signal
return slot_func
和一个单独的类装饰器来包装class.__init__
方法:
from inspect import getmembers, isfunction
def connectsignals(cls):
signal_handlers = getmembers(
cls, lambda m: isfunction(m) and hasattr(m, '_signal_handler'))
init = getattr(cls, '__init__', lambda self: None)
def wrapper(self, *args, **kwargs):
init(self, *args, **kwargs)
for name, handler in signal_handlers:
handler._signal_handler.connect(handler.__get__(self))
cls.__init__ = wrapper
return cls
装饰类和信号处理程序:
@connectsignals
class manager(object):
@connector(update)
def update(self):
print("I'm updating, yay!!!!")
每次创建新实例时,装饰器都会连接所有处理程序:
>>> class Signal(object):
... def connect(self, handler):
... print('connecting {!r}'.format(handler))
...
>>> update = Signal()
>>> @connectsignals
... class manager(object):
... @connector(update)
... def update(self):
... print("I'm updating, yay!!!!")
...
>>> manager()
connecting <bound method manager.update of <__main__.manager object at 0x105439ac8>>
<__main__.manager object at 0x105439ac8>
您可能想要检查signalslot
项目是否使用弱引用来跟踪信号处理程序,因为您要么对您创建的任何实例存在循环引用问题(其中manager
实例保持活动,因为信号仍然引用该实例的绑定方法),或者您的信号处理程序过早清理,因为绑定的方法存储在弱引用中,因此不会有任何其他引用以保持它们存活。
查看signalslot
source code,我看到项目的当前迭代使用了硬引用,因此除非您明确地执行此操作,否则永远不会清除manager
个实例。仅仅因为这个原因,我避免使用方法作为信号处理程序。如果您想使用弱引用,请查看using python WeakSet to enable a callback functionality。