我能弱引用方法吗?

5

可能是重复问题:
为什么该绑定方法无法使用弱引用?

一些背景信息:

我试图实现一个监听器模式(或者观察者模式):一个事件管理器维护了所有对事件感兴趣的监听器处理程序的列表。例如,一个监听器对象将会有一个onEndOfTheWorldEvent方法,每当一个EndOfTheWorldEvent类的实例被发布时,该方法都会被事件管理器调用。很简单。

但是,我想要使用弱引用来引用处理程序,因为当不再需要监听器时,我不希望事件管理器保持我的处理程序(绑定方法)处于活动状态。

所以我想到“让我们把所有处理程序放在WeakSet中”。但我无法使其工作。

下面是代码(或者说是最小化后的代码,这里只有一种事件和一种处理程序):

#! /usr/bin/python
"""

"""
import sys
import weakref

class Listener(object):
    def handler(self, event):
        print event

class EventManager(object):
    def __init__(self):
        self.handlers = weakref.WeakSet()
    def register(self, listener):
        print "Registering..."
        self.handlers.add(listener.handler)
        CountRefs(listener.handler)
        print "Number of handlers registered:", len(self.handlers)
        print "Registered."

def CountRefs(what):
    print "Hard count:", sys.getrefcount(what)
    print "Weak count:", weakref.getweakrefcount(what)

listener = Listener()
em = EventManager()
CountRefs(listener.handler)
em.register(listener)
CountRefs(listener.handler)

结果:

Hard count: 3
Weak count: 0
Registering...
Hard count: 3
Weak count: 0
Number of handlers registered: 0
Registered.
Hard count: 3
Weak count: 0

看起来从来没有弱引用,集合始终为空。

为了让它更简单:

>>> class C(object):
>>>     def blah(self):
>>>         print "blah"
>>> 
>>> c = C()
>>> w = weakref.ref(c.blah)
>>> print w
<weakref at 0x11e59f0; dead>

我能否完全无法创建方法的弱引用?如果不能,为什么

所以我猜解决方法是用WeakKeyDictionary代替WeakSet:键是监听器本身,值是处理程序。事实上,我可以弱引用我的Listeners。但这使得数据结构变得有点复杂,并且在广播事件给所有人时,需要经过该结构中的另一个层次。

你认为呢?


这个问题并不是真正的重复,因为它关注的是解决方案,而不仅仅是“为什么不”。更不用说这里有一个很好的被接受的答案。最多你可以将其他问题标记为重复,意思是:“这个问题暗示了对另一个问题的答案”。 - kxr
2个回答

8

假设您想要在方法“meth”上使用弱引用。

您可以通过以下方式获得其弱引用

weak_obj = weakref.ref(meth.im_self)
weak_func = weakref.ref(meth.im_func)

因此,您可以像这样对其进行取消引用。
obj = weak_obj()
func = weak_func()

使用以下代码可以获取“meth”:

meth = getattr(obj, func.__name__)

哇,真是个好技巧。我以前从没需要查看过这些 im_selfco - Niriel
实际上,我需要这个,因为绑定方法引用了实例,因此实例不会从WeakKeyDictionary中消失。谢谢! - Niriel
3
看起来从Python 2.6开始,对于这些对象,首选访问方式是使用bound_method.__self__来获取对象,使用bound_method.__func__来获取函数。 - user890167

2

listener.handler每次都会给你一个新的绑定函数的引用。因此它几乎立即被垃圾回收。


实际上,“is”的比较始终返回False。然后,我又想可能只是一种奇怪的约定。 - Niriel
@Neil,您能否详细说明一下listener.handler以及如何使用它为函数提供新的绑定引用?先谢谢了! - AhmedWas
@AhmedWas,我不确定你在问什么。listener.handler是一个表达式,它返回一个函数,该函数调用原始函数(在类中声明),但始终绑定到给定的实例。只是碰巧这个表达式每次都会给您一个新的绑定引用,这在原始问题的上下文中并不有帮助。 - Neil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接