The joys of weakrefs... references to instance methods (callbacks)

Mike C. Fletcher mcfletch at geocities.com
Sat Feb 9 12:56:49 CET 2002


Sat down to work out a model-view framework for a little application and 
had one of those "oh, darn, didn't think that out very carefully" 
moments.  You see, I was planning on doing a fairly straightforward 
callback-passing mechanism:

     WatchAble.addWatcher( callback )

With callback getting a nicely formatted call, giving both the object 
and an event object from which it could extract all kind of nice "what 
type of event" occured information.

Now, of course, being a good little boy (evidence to the contrary), I 
was going to use weakref.ref objects to hold the callbacks, so that when 
the view objects are destroyed, their callbacks will no longer get 
called (since that causes Nasties(TM)).

It's a strange thing, but the most convenient callback is almost allways 
a bound method of a view object:

     myModelObject.addWatcher( myViewObject.OnModelUpdated )

myViewObject.OnModelUpdated is a _new_ Python object (a bound method), 
with only the reference in the calling stack keeping it alive.  The 
weakref.ref( Which is, of course, where everything goes straight south 
in a handbasket.  myViewObject.OnModelUpdated is actually a new Python 
objectmyViewObject.OnModelUpdated ) call (in addWatcher) will generate 
the reference object.  That reference immediately goes to "dead" status 
as the line of code after addWatcher is executed.

Well, there's an obvious solution, I can create an explicit callback 
object, store it instead of the weakref, and let it do the 
de-referencing to get the method (as long as the instance and function 
are both still available). Basically, I need to wrap weakref.ref like so:

import weakref, types, new

class MethodRef:
     """Weak-reference to a method that takes the component parts of the 
object, rather than the object itself for the "live" definition"""
     def __init__( self, object ):
         self.instance = weakref.ref( object.im_self )
         self.function = weakref.ref( object.im_func )
     def __call__( self ):
         instance = self.instance()
         function = self.function()
         if instance and function:
             return new.instancemethod( function, instance, 
instance.__class__ )
         else:
             return None

def ref( object ):
     if type(object) == types.MethodType:
         return MethodRef( object )
     else:
         return weakref.ref( object )

Yes, it's slightly different semantics than weakref, but it's actually 
useful in the real world ;) .  Anyway, thought I'd share the code so 
that people can object that it's a tool of the devil :) .  If the powers 
that be want to point out any reasons why I shouldn't do this, would 
love to hear it before my house of cards goes *phoosh*.

Enjoy all,
Mike
_______________________________________
   Mike C. Fletcher
   http://members.rogers.com/mcfletch/






More information about the Python-list mailing list