2014-02-17 26 views
12

我正在研究是否可以在python中實現簡單的回調函數。我認爲我可以使用weakref.WeakSet來做這件事,但顯然有一些我錯過了或者被誤解了。正如你在代碼中看到的,我首先嚐試了'ClassA'對象中的回調方法列表,但是意識到這會使已添加到回調列表的對象保持活動狀態。相反,我嘗試使用weakref.WeakSet,但也沒有辦法(至少不是這樣)。最後四行代碼中的評論解釋了我想要發生的事情。使用python WeakSet啓用回調函數

任何人都可以幫助我嗎?

from weakref import WeakSet 
class ClassA: 
    def __init__(self): 
     #self.destroyCallback=[] 
     self.destroyCallback=WeakSet() 
    def __del__(self): 
     print('ClassA object %d is being destroyed' %id(self)) 
     for f in self.destroyCallback: 
      f(self) 
class ClassB: 
    def destroyedObjectListener(self,obj): 
     print('ClassB object %d is called because obj %d is being destroyed'%(id(self),id(obj))) 
a1=ClassA() 
a2=ClassA() 
b=ClassB() 

a1.destroyCallback.add(b.destroyedObjectListener) 
#a1.destroyCallback.append(b.destroyedObjectListener) 
print('destroyCallback len() of obj: %d is: %d'%(id(a1),len(a1.destroyCallback))) # should be 1 

a2.destroyCallback.add(b.destroyedObjectListener) 
#a2.destroyCallback.append(b.destroyedObjectListener) 
print('destroyCallback len() of obj: %d is: %d'%(id(a2),len(a2.destroyCallback))) # should be 1 

del a1 # Should call b.destroyedObjectListener(self) in its __del__ method 

del b # should result in no strong refs to b so a2's WeakSet should automatically remove added item 

print('destroyCallback len() of obj: %d is: %d'%(id(a2),len(a2.destroyCallback))) # should be 0 
del a2 # Should call __del__ method 

UPDATE:基於公認的答案解決方案可以在GitHub上找到:[email protected]:thgis/PythonEvent.git

回答

15

您不能創建到方法的對象弱引用。方法對象是短命;它們是在您訪問實例名稱時即時創建的。請參閱descriptor howto這是如何工作的。

當您訪問的方法名稱,方法對象爲您創建,當你再添加方法到WeakSet,引用不存在到它了,所以垃圾收集愉快地再次清除它。

你將不得不存儲更短暫的東西。存儲實例對象本身會的工作,然後調用已註冊的回調預定義的方法:

def __del__(self): 
    for f in self.destroyCallback: 
     f.destroyedObjectListener(self) 

並註冊:

a1.destroyCallback.add(b) 

您也可以b本身可調用給它一個__call__方法:

class ClassB: 
    def __call__(self,obj): 
     print('ClassB object %d is called because obj %d ' 
       'is being destroyed' % (id(self), id(obj))) 

另一種方法是存儲對unde的引用rlying函數對象加一個參考實例:

import weakref 


class ClassA: 
    def __init__(self): 
     self._callbacks = [] 

    def registerCallback(self, callback): 
     try: 
      # methods 
      callback_ref = weakref.ref(callback.__func__), weakref.ref(callback.__self__) 
     except AttributeError: 
      callback_ref = weakref.ref(callback), None 
     self._callbacks.append(callback_ref) 

    def __del__(self): 
     for callback_ref in self._callbacks: 
      callback, arg = callback_ref[0](), callback_ref[1] 
      if arg is not None: 
       # method 
       arg = arg() 
       if arg is None: 
        # instance is gone 
        continue 
       callback(arg, self) 
       continue 
      else: 
       if callback is None: 
        # callback has been deleted already 
        continue 
       callback(self) 

演示:

>>> class ClassB: 
...  def listener(self, deleted): 
...   print('ClassA {} was deleted, notified ClassB {}'.format(id(deleted), id(self))) 
... 
>>> def listener1(deleted): 
...  print('ClassA {} was deleted, notified listener1'.format(id(deleted))) 
... 
>>> def listener2(deleted): 
...  print('ClassA {} was deleted, notified listener2'.format(id(deleted))) 
... 
>>> # setup, one ClassA and 4 listeners (2 methods, 2 functions) 
... 
>>> a = ClassA() 
>>> b1 = ClassB() 
>>> b2 = ClassB() 
>>> a.registerCallback(b1.listener) 
>>> a.registerCallback(b2.listener) 
>>> a.registerCallback(listener1) 
>>> a.registerCallback(listener2) 
>>> 
>>> # deletion, we delete one instance of ClassB, and one function 
... 
>>> del b1 
>>> del listener1 
>>> 
>>> # Deleting the ClassA instance will only notify the listeners still remaining 
... 
>>> del a 
ClassA 4435440336 was deleted, notified ClassB 4435541648 
ClassA 4435440336 was deleted, notified listener2 
+0

我現在有你的建議,它似乎很好。我已經將解決​​方案包裝在一個Event類中,並且只實現了add(),remove()和signal()以最小限度地使用。也許它會擴大。如果有人願意,可以使用github從github上下載解決方案:[email protected]:thgis/PythonEvent.git – thomas

1

請嘗試以下變化:

要更新WeakSet:

a1.destroyCallback.add(b)

所以WeakSet保持至b的參考。

然後在ClassA的的__del__方法,觸發回調是這樣的:

for f in self.destroyCallback: 
     f.destroyedObjectListener(self) 
+0

感謝您的答覆格倫。 這將工作我猜..我認爲我只能創建一個weakref對象而不是我試過的對象方法。你的方式將創建一個weakref對b,因此工作,但不得不明確寫入函數名稱「destroyedObjectListener」事件驅動的想法消失。但不是,你越少,我就得出這樣的結論:問題是我試圖爲類方法創建一個weakref。 – thomas