|I l@ve RuBoard|
5.15 Keeping References to Bound Methods Without Inhibiting Garbage Collection
Credit: Joseph A. Knapka
Weak references were an important addition to Python 2.1, but they're not directly usable for bound methods, unless you take some precautions. To allow an object to be garbage-collected despite outstanding references to its bound methods, you need some wrappers. Put the following in the weakmethod.py file:
import weakref class _weak_callable: def _ _init_ _(self, obj, func): self.im_self = obj self.im_func = func def _ _call_ _(self, *args, **kws): if self.im_self is None: return self.im_func(*args, **kws) else: return self.im_func(self.im_self, *args, **kws) class WeakMethod: """ Wraps a function or, more importantly, a bound method in a way that allows a bound method's object to be GCed, while providing the same interface as a normal weak reference. """ def _ _init_ _(self, fn): try: self._obj = weakref.ref(fn.im_self) self._meth = fn.im_func except AttributeError: # It's not a bound method self._obj = None self._meth = fn def _ _call_ _(self): if self._dead( ): return None return _weak_callable(self._getobj( ), self._meth) def _dead(self): return self._obj is not None and self._obj( ) is None def _getobj(self): if self._obj is None: return None return self._obj( )
>>> class C: ... def f(self): ... print "Hello" ... def _ _del_ _(self): ... print "C dying" ... >>> c = C( ) >>> cf = c.f >>> del c # c continues to wander about with glazed eyes... >>> del cf # ...until we stake its bound method, only then it goes away: C dying
Sometimes that isn't what you want. For example, if you're implementing an event-dispatch system, it might not be desirable for the mere presence of an event handler (a bound method) to prevent the associated object from being reclaimed. A normal weakref.ref to a bound method doesn't quite work the way one might expect, because bound methods are first-class objects. Weak references to bound methods are dead-on-arrival, i.e., they always return None when dereferenced, unless another strong reference to the same bound method exists. The following code, for example, doesn't print "Hello" but instead raises an exception:
>>> from weakref import * >>> c = C( ) >>> cf = ref(c.f) >>> cf # Oops, better try the lightning again, Igor... <weakref at 80ce394; dead> >>> cf()( ) Traceback (most recent call last): File "", line 1, in ? TypeError: object of type 'None' is not callable
>>> from weakmethod import * >>> cf = WeakMethod(c.f) >>> cf()( ) # It LIVES! Bwahahahaha! Hello >>> del c # ...and it dies C dying >>> print cf( ) None
A known problem is that _weak_callable and WeakMethod don't provide exactly the same interface as normal callables and weak references. To return a normal bound method, we can use new.instancemethod (from the standard module new), but for that purpose, WeakMethod should also find out and memorize the class in which the weakly held bound method is defined.
5.15.4 See Also
The Library Reference section on the weakref module.
|I l@ve RuBoard|