2016-04-04 170 views
3

考虑以下装饰:适用装饰类类方法

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() missing 1 required positional argument: 'self'

更具体地说,如果我试图发出信号:

update.emit() 

是的,我在该项目中使用"signalslot"

回答

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,我发现当前项目迭代使用了严格的引用,因此除非明确这样做,否则您的实例永远不会被清除。仅仅因为这个原因,我会避免使用方法作为信号处理程序。如果您想使用弱引用,请查看using python WeakSet to enable a callback functionality