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