[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