簡體   English   中英

將decorator類應用於類方法

[英]apply decorator class to class method

考慮以下裝飾者:

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”

self必須是位置參數,而不是關鍵字參數:

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

如果需要區分函數和方法,請改為實現描述符

但是,如果您嘗試連接信號,則需要對每個實例上的綁定方法執行此操作。 您最好在實例創建時連接信號:

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

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

當調用manager.__init__ ,你有一個新的實例,然后你就可以創建一個 self.update bound方法來接收信號。

你仍然可以使用裝飾器,但是你可以在類級別注冊哪些函數可以充當信號處理程序; 您必須在實例創建時枚舉類中的所有函數,然后綁定所有這些信號:

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源代碼 ,我看到項目的當前迭代使用了硬引用,因此除非您明確地執行此操作,否則永遠不會清除manager實例。 僅僅因為這個原因,我會避免使用方法作為信號處理程序。 如果您想使用弱引用,請查看使用python WeakSet啟用回調功能

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM