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