Reference to a bound method that permits the associated object to be garbage collected.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # File weakmethod.py
from weakref import *
class _weak_callable:
def __init__(self,obj,func):
self._obj = obj
self._meth = func
def __call__(self,*args,**kws):
if self._obj is not None:
return self._meth(self._obj,*args,**kws)
else:
return self._meth(*args,**kws)
def __getattr__(self,attr):
if attr == 'im_self':
return self._obj
if attr == 'im_func':
return self._meth
raise AttributeError, attr
class WeakMethod:
""" Wraps a function or, more importantly, a bound method, in
a way that allows a bound method's object to be GC'd, while
providing the same interface as a normal weak reference. """
def __init__(self,fn):
try:
self._obj = 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._obj(),self._meth)
def _dead(self):
return self._obj is not None and self._obj() is None
|
A normal bound method "hides" 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:
<pre>
>>> 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 and arms outstretched...
>>> del cf # ...until we stake its bound method.
C dying
>>>
</pre>
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. Normal weakref.refs to bound methods don't quite work the way one expects, because bound methods are first-class objects; weakrefs to bound methods are dead-on-arrival unless some other strong reference to the same bound method exists. The following code, for example, doesn't print "Hello"; rather it raises an exception:
<pre>
>>> from weakref import *
>>> c = C()
>>> cf = ref(c.f)
>>> cf
<weakref at 80ce394; dead> # Oops, better try the lightning again, Igor...
>>> cf()()
Traceback (most recent call last):
File "", line 1, in ?
TypeError: object of type 'None' is not callable
>>>
</pre>
WeakMethod allows you to have weak references to bound methods in a useful way:
<pre>
>>> from weakmethod import *
>>> cf = WeakMethod(c.f)
>>> cf()() # It LIVES! Bwahahahaha!
Hello
>>> del c # ...and it dies.
C dying
>>> print cf()
None
>>>
</pre>
Known problems: _weak_callable and WeakMethod don't provide exactly the same interface as normal callables and weak references. There may be a way for WeakMethod to return a normal bound method rather than a _weak_callable, but I haven't yet found it.
PEP 205 discusses the rationale for weak references as they're implemented by the weakref module.
Simpler implementation. It is not exactly the same thing, but I think it's simpler for specific uses:
Output is:
Simpler implementation. Hum, of course it's better to handle error correctly.
Replace
with
Simpler implementation (without typo). Sorry, I've missed the dereference on self.c. The corrected class is:
Alternative implementation. Here is a version (hopefully the last from me) which support both free/bounded method:
Example:
Another version. Here's another version, that uses module new to return a true bound-method, and works with functions and unbounded methods also.