[Python-ideas] Weak-referencing/weak-proxying of (bound) methods
Jan Kaliszewski
zuo at chopin.edu.pl
Mon Jun 11 01:16:29 CEST 2012
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?
Cheers.
*j
More information about the Python-ideas
mailing list