[Python-ideas] Weak-referencing/weak-proxying of (bound) methods
Tal Einat
taleinat at gmail.com
Fri Jun 15 14:41:10 CEST 2012
On Mon, Jun 11, 2012 at 2:16 AM, Jan Kaliszewski <zuo at chopin.edu.pl> wrote:
> Hello,
>
> Today, I encountered a surprising bug in my code which creates
> some weakref.proxies to instance methods... The actual Python
> behaviour related to the issue can be ilustrated with the
> following example:
>
> >>> import weakref
> >>> class A:
> ... def method(self): print(self)
> ...
> >>> A.method
> <function method at 0xb732926c>
> >>> a = A()
> >>> a.method
> <bound method A.method of <__main__.A object at 0xb7326bec>>
> >>> r = weakref.ref(a.method) # creating a weak reference
> >>> r # ...but it appears to be dead
> <weakref at 0xb7327d9c; dead>
> >>> w = weakref.proxy(a.method) # the same with a weak proxy
> >>> w
> <weakproxy at 0xb7327d74 to NoneType at 0x829f7d0>
> >>> w()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> ReferenceError: weakly-referenced object no longer exists
>
> This behaviour is perfectly correct -- but still surprising,
> especially for people who know little about method creation
> machinery, descriptors etc.
>
> I think it would be nice to make this 'trap' less painful --
> for example, by doing one or both of the following:
>
> 1. Describe and explain this behaviour in the weakref
> module documentation.
>
> 2. Provide (in functools?) a type-and-decorator that do the
> same what func_descr_get() does (transforms a function into
> a method) *plus* caches the created method (e.g. at the
> instance object).
>
> A prototype implementation:
>
> class InstanceCachedMethod(object):
>
> def __init__(self, func):
> self.func = func
> (self.instance_attr_name
> ) = '__{0}_method_ref'.format(func.__name__)
>
> def __get__(self, instance, owner):
> if instance is None:
> return self.func
> try:
> return getattr(instance, self.instance_attr_name)
> except AttributeError:
> method = types.MethodType(self.func, instance)
> setattr(instance, self.instance_attr_name, method)
> return method
>
> A simplified version that reuses the func.__name__ (works well
> as long as func.__name__ is the actual instance attribute name...):
>
> class InstanceCachedMethod(object):
>
> def __init__(self, func):
> self.func = func
>
> def __get__(self, instance, owner):
> if instance is None:
> return self.func
> method = types.MethodType(self.func, instance)
> setattr(instance, self.func.__name__, method)
> return method
>
> Both versions work well with weakref.proxy()/ref() objects:
>
> >>> class B:
> ... @InstanceCachedMethod
> ... def method(self): print(self)
> ...
> >>> B.method
> <function method at 0xb7329d6c>
> >>> b = B()
> >>> b.method
> <bound method B.method of <__main__.B object at 0xb7206ccc>>
> >>> r = weakref.ref(b.method)
> >>> r
> <weakref at 0xb72c611c; to 'method' at 0xb736c40c (method)>
> >>> w = weakref.proxy(b.method)
> >>> w
> <weakproxy at 0xb7327e14 to method at 0xb736c40c>
> >>> w()
> <__main__.B object at 0xb7206ccc>
>
> What do you think about it?
>
I was bitten by this issue a while ago as well. It made working with
weakref proxies much more involved than I expected it would be.
Wouldn't it be better to approach the issue from the opposite end, and
improve/wrap/replace weakref.proxy with something that can handle bound
methods?
- Tal
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20120615/9a377bac/attachment.html>
More information about the Python-ideas
mailing list