3

folgendes Dekorateur Bedenken Sie:gelten Dekorateur Klasse zu Klasse Methode

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) 

Und folgendes Signal und Klasse mit Methode, die wir dekorieren müssen:

from signalslot import Signal 

update = Signal() 

class manager(object): 
    # SOME CODE CUT 
    @connector(update) 
    def update(self): 
     print("I'm updating, yay!!!!") 

Wie man sehen kann ich Dekorateur einige passieren müssen zusätzliche Argumente, und in diesem Fall - Signal muss ich verbinden. Wie man selbst auch übergeben?

Der Grund, warum ich das fragen, weil es mit folgenden Fehler schlägt fehl, wenn ich versuchen, diese Dekorateur Methode anzuwenden, keine Funktion:

TypeError: update() missing 1 required positional argument: 'self'

Genauer gesagt, wenn ich versuche, das Signal auszusenden:

update.emit() 

Und ja, ich verwende "signalslot" in diesem Projekt.

Antwort

2

self muss ein Positions Argument sein, kein Schlüsselwort Argument:

def wrapper(self, *args, **kwargs): 
    #  ^^^^ You can't use "self=None" here. 
    slot_func(self, *args, **kwargs) 

Wenn Sie zwischen Funktionen und Methoden unterscheiden müssen, implementieren eine descriptor statt.

Wenn Sie jedoch versuchen, ein Signal zu verbinden, müssen Sie eine gebundene Methode für jede Instanz . Sie würden besser Ihre Signale bei Beispiel Erstellungszeit verbindet:

class manager(object): 
    def __init__(self): 
     update.connect(self.update) 

    def update(self): 
     print("I'm updating, yay!!!!") 

Wenn manager.__init__ genannt wird, haben Sie eine neue Instanz haben, und es ist, dann können Sie einself.update gebundene Methode erstellen die erhalten Signal.

Sie können immer noch Decorator dafür verwenden, aber Sie können am besten auf Klassenebene registrieren, welche Funktionen als Signalhandler fungieren können; Sie müssten alle Funktionen auf Ihrer Klasse bei instanzErstellungsZeit aufzuzählen und binden alle diese Signale dann:

class connector(object): 
    def __init__(self, signal): 
     self.signal = signal 
    def __call__(self, slot_func): 
     slot_func._signal_handler = self.signal 
     return slot_func 

und eine separate Klasse Dekorateur die class.__init__ Methode wickeln:

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 

Verzieren der Klasse sowie die Signal-Handler:

@connectsignals 
class manager(object): 
    @connector(update) 
    def update(self): 
     print("I'm updating, yay!!!!") 

Der Dekorateur dann alle Handler verbindet jedes Mal, wenn eine neue Instanz erstellt:

>>> 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> 

Möglicherweise mögen Sie überprüfen, ob das signalslot Projekt schwachen Verweis verwendet Signal-Handler jedoch zu verfolgen, wie Sie entweder ein Problem des Kreis Verweise auf alle Instanzen, die Sie erstellt haben werden (wo eine manager Instanz gehalten wird lebendig, weil ein Signal verweist immer noch auf eine gebundene Methode zu dieser Instanz), oder wo Ihre Signalhandler zu früh bereinigt werden, weil Ihre gebundenen Methoden in schwachen Referenzen gespeichert sind und daher keine anderen Referenzen haben, um sie am Leben zu halten.

Mit Blick auf die signalslot source code, sehe ich, dass die aktuelle Iteration des Projekts verwendet harte Referenzen, so dass Ihre manager Instanzen werden nie gelöscht werden, wenn Sie dies explizit tun. Aus diesem Grund würde ich es vermeiden, Methoden als Signalhandler zu verwenden. Werfen Sie einen Blick auf using python WeakSet to enable a callback functionality, wenn Sie stattdessen schwache Referenzen verwenden möchten.