使用python WeakSet启用回调功能 [英] using python WeakSet to enable a callback functionality

查看:407
本文介绍了使用python WeakSet启用回调功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在调查是否可以在python中实现一个简单的回调函数。我认为我可以使用weakref.WeakSet这个,但有一些我缺失或有误解的东西。正如你可以在代码中看到的,我首先尝试了在ClassA对象中的回调方法列表,但意识到这将保持已添加到回调列表中的对象活着。相反我尝试使用weakref.WeakSet,但是没有做的伎俩(至少不是这样)。

I'm investigating if I can implement an easy callback functionality in python. I thought I might be able to use weakref.WeakSet for this, but there is clearly something I'm missing or have misunderstood. As you can see in the code I first tried with a list of call back methods in 'ClassA' objects, but realized that this would keep objects that have been added to the list of callbacks alive. Instead I tried using weakref.WeakSet but that doesnt do the trick either (at least not en this way). Comments in the last four lines of code explain what I want to happen.

任何人都可以帮我解决这个问题?

Can anyone help me with this?

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上找到:git@github.com:thgis / PythonEvent.git

UPDATE: solution based on the accepted answer can be found on github: git@github.com:thgis/PythonEvent.git

推荐答案

您不能创建对方法对象的弱引用。方法对象是短暂的;它们是在您访问实例上的名称时即时创建的。请参阅描述符howto 的工作原理。

You cannot create weak references to method objects. Method objects are short lived; they are created on the fly as you access the name on the instance. See the descriptor howto how that works.

当您访问方法名称时,会为您创建一个方法对象,然后当您将该方法添加到 WeakSet 时,引用存在于它,所以垃圾收集很高兴再次清理它。

When you access a method name, a new method object is created for you, and when you then add that method to the WeakSet, no other references exist to it anymore, so garbage collection happily cleans it up again.

你必须存储一些不太短暂的东西。存储实例对象本身将工作,然后调用注册的回调上的预定义方法:

You'll have to store something less transient. Storing instance objects themselves would work, then call a predefined method on the registered callbacks:

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

a1.destroyCallback.add(b)

您还可以通过给予来使 b __call __ 方法:

You can also make b itself a callable by giving it a __call__ method:

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

另一种方法是存储对底层函数对象的引用以及对实例的引用:

Another approach would be to store a reference to the underlying function object plus a reference to the instance:

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)


b $ b

演示:

Demo:

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

这篇关于使用python WeakSet启用回调功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆