weakrefs and bound methods

Mathias Panzenboeck e0427417 at student.tuwien.ac.at
Thu Oct 11 15:05:56 EDT 2007


Bruno Desthuilliers wrote:
> Mathias Panzenboeck a écrit :
> 
> About the lost weakref problem: in Python, methods are just tiny
> wrappers around the object, class and function created at lookup time
> (yes, on *each* lookup) (and WWAI, they are by the function object
> itself, which implements the descriptor protocol).
> 
>> When I change the class Wrapper to following, the class Foo works:
>>
>> class Wrapper(object):
>>     def __init__(self,x):
>>         self.func_name = x.func_name
>>         self.x         = weakref.ref(x.im_self)
> 
>>     def __call__(self,*args,**kwargs):
>>         x = self.x()
>>         if x is None:
>>             print "lost reference"
>>         else:
>>             return getattr(x,self.func_name)(*args,**kwargs)
> 
> Anyway, the whole thing looks quite baroque to me - and I'm known for my
> immoderate appetite for brain-melting features....
> 
> If I understand correctly, you're doing all this to avoid circular
> references while keeping track of a list of methods to call ?
> 
> If yes, then you're thru much pain for nothing. Mostly because there are
>  other much more simpler solutions. The first one is obviously to use
> names and getattr. In it's most naïve implementation, this would look like:
> 
> class Foo(object):
>     def __init__(self):
>     self._methods = set()
>     self._methods.add('_foo')
> 
>     def _foo(self):
>     print "_foo"
> 
>     def callMethods(self):
>      for name in self._methods:
>              meth = getattr(self, name, None)
>              if callable(meth):
>                  meth()
> 
> 
> 
> 
> Now if _methods is supposed to have the same content class-wise (which
> is the case in your exemple), no need to have it as an instance method:
> 
> class Foo(object):
>     _methods = set('_foo')
> 
>     def _foo(self):
>     print "_foo"
> 
>     def callMethods(self):
>      for name in self._methods:
>              meth = getattr(self, name, None)
>              if callable(meth):
>                  meth()
> 
> We could go further, like using a decorator to mark methods to add to
> _methods, and a metaclass to set the whole thing up, but this would
> probably be overkill. Unless there are other requirements, I'd
> personnaly stop here.
> 
> Oh, and yes, while we're at it : Python usually knows how to deal with
> circular references....
> 

Well yes and no. It does with the enabled gc but I like refcounting much more because
of the predictable behaviour. And when there are no refloops, refcounting makes no
problems. However, Alex Martelli already showed a great way to do what I need, see:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81253

I need it for a little UI lib. Well lib is too much. Just a small wrapper around
blenders unusable API for building GUIs for your scripts. It's ugly as hell.
I made very few classes to wrap up that crap. Every input widget in my UI lib has an
action. You can register action listeners like this:

def actionListener(value):
	print "new value:",value

widget.addActionListener(actionListener)

When an widget itself want's to listen for it's action and does this in __init__:

	self.addActionListener(sefl.__actionListener)

there would be a ref loop. Therefore I do this now:

	self.addActionListener(WeakListener(sefl.__actionListener))

And WeakListener I implemented as follows:

class WeakListener(object):
	__slots__ = ('__weakref__', '_func','_obj')

	def __init__(self,listener):
		self._func = listener.im_func
		self._obj  = weakref.ref(listener.im_self)
	
	def __call__(self,*args,**kwargs):
		obj = self._obj()

		if obj is None:
			print "*** lost reference to %s's self object ***" % \
				self._func.func_name
		else:
			self._func(obj,*args,**kwargs)


Could be better/more generic, but that's all I need.

To implement the action listener by overloading a action method is IMHO a bad idea.
Because that would kill the listener of the base class. Explicitly calling the base
classes action method is ugly and error prone. And when I have to register action
listeners from "outside" the widget, too.

But thanks for your reply. :)

	-panzi



More information about the Python-list mailing list