5.15 Keeping References to Bound Methods Without Inhibiting Garbage Collection


Credit: Joseph A. Knapka

5.15.1 Problem

You want to hold bound methods, while still allowing the associated object to be garbage-collected.

5.15.2 Solution

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

5.15.3 Discussion

A normal bound method holds a strong reference to the bound method's object. That means that the object can't be garbage-collected until the bound method is disposed of:

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

WeakMethod allows you to have weak references to bound methods in a useful way:

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



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2005
Pages: 346

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net