homepage

This issue tracker has been migrated to GitHub , and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Instance methods and WeakRefs don't mix.
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Sundance, daniel.urban, jcea, mark.dickinson, pitrou, python-dev, sfeltman
Priority: normal Keywords: patch

Created on 2012年04月20日 13:49 by Sundance, last changed 2022年04月11日 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
WeakCallableRef.py Sundance, 2012年04月20日 13:49
weakmethod.patch pitrou, 2012年11月11日 19:07 review
Messages (12)
msg158828 - (view) Author: (Sundance) Date: 2012年04月20日 13:49
SUMMARY
=======
The behavior of instance methods makes it impossible to keep a weakref on them. The reference dies instantly even if the instance and the method still exist.
EXAMPLE
=======
>>> import weakref
>>> callbacks = weakref.WeakSet()
>>> def callback1():
... print "In callback1."
>>> class SomeClass:
... def callback2(self):
... print "In callback2."
>>> some_instance = SomeClass()
>>> callbacks.add( callback1 )
>>> callbacks.add( some_instance.callback2 )
>>> for callback in callbacks:
... callback()
In callback1.
>>> ## callback2 is never called!
ANALYSIS
========
The WeakSet in the example above, and the weakref.ref() it employs, actually behave as specified. It's the particular nature of bound methods that causes the unexpected behavior.
From what I understand, instance methods are bound dynamically when looked up on the instance. A new object of type instancemethod is created each time:
>>> t1 = some_instance.callback
>>> t2 = some_instance.callback
>>> t1 is t2
False
So when a program calls weakref.ref(some_instance.callback), a new instancemethod object is created on the fly, a weakref to that object is created... and the instancemethod object dies, because its refcount is 0.
This fundamental difference between instance methods and other callables makes it painful to implement weakly referencing callback registries.
PROPOSED SOLUTION
=================
Changing the fundamental nature of instance methods to accommodate one single corner case doesn't seem worthwhile.
Similarly, changing the behavior of weakref.ref(), which does work as specified, is probably not a good idea.
My approach is thus to provide a new helper, WeakCallableRef, that behaves like weakref.ref(), but is safe to use on callables and does what you would naturally expect with instance methods.
It works by binding the lifetime of the ref to that of 1/ the instance bearing the method, and 2/ the unbound method itself. It is also safe to use on other function types beside instance methods, so implementations of callback registries don't have to special case depending on the callable type.
The unexpected behavior should also be mentioned somewhere in the weakref documentation, by the way.
See attached file for a proposed implementation.
msg158834 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012年04月20日 15:16
I quite like the WeakCallableRef idea, having had to work around this problem myself in the past (using a similar mechanism).
This looks like a feature request rather than a bug report, though; changing Type and Versions accordingly.
msg175343 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012年11月11日 10:18
Adding some useful links from Simon Feltman in issue 16452.
See also:
http://bugs.python.org/issue813299
http://bugs.python.org/issue7464
This is the recipes:
http://code.activestate.com/recipes/81253/
http://mindtrove.info/python-weak-references/ 
msg175344 - (view) Author: Simon Feltman (sfeltman) Date: 2012年11月11日 10:20
Just a note this is also referred to as a "WeakMethod" by some folks (so this ticket shows up in those searches). See also:
http://bugs.python.org/issue813299
http://bugs.python.org/issue7464
http://bugs.python.org/issue16452 
msg175372 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012年11月11日 17:16
WeakMethod sounds like a good name to me. However, instead of acting as a proxy, I think it should act as a regular weakref (i.e. you have to call it to get the actual method object).
msg175384 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012年11月11日 19:07
Here is a patch adding a WeakMethod class. It still lacks docs, I'll add some if people agree on the principle.
msg175391 - (view) Author: Simon Feltman (sfeltman) Date: 2012年11月11日 21:23
The WeakCallableRef that was attached seemed to support regular functions (or anything callable) which is nice. The naming also leaves room for a WeakCallableProxy.
msg175392 - (view) Author: Simon Feltman (sfeltman) Date: 2012年11月11日 21:34
Some more complex examples from various libraries:
https://github.com/django/django/blob/master/django/dispatch/saferef.py
https://github.com/11craft/louie/blob/master/louie/saferef.py
I think both of these originated in pydispatcher.
msg175474 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012年11月12日 21:32
The patch looks okay to me.
What does inheriting from 'ref' buy you? This feels a bit strange to me: the way I think of it, the WeakMethod *has* a weakref to the underlying object, rather than *being* a weakref to the underlying object. The __repr__ also seems a bit misleading as a result:
>>> o = Object()
>>> m = o.some_method
>>> WeakMethod(m)
<weakref at 0x100665ae0; to 'Object' at 0x101115840>
msg175475 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012年11月12日 21:34
> What does inheriting from 'ref' buy you?
Hmm, I'm not sure. I thought I'd mimick KeyedRef's inheritance design, plus isinstance(..., weakref.ref) works, and composition would make the object slightly bigger. Other than that, probably nothing.
msg175792 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2012年11月17日 18:00
New changeset 27c20650aeab by Antoine Pitrou in branch 'default':
Issue #14631: Add a new :class:`weakref.WeakMethod` to simulate weak references to bound methods.
http://hg.python.org/cpython/rev/27c20650aeab 
msg175793 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2012年11月17日 18:01
Committed, thank you!
History
Date User Action Args
2022年04月11日 14:57:29adminsetgithub: 58836
2012年11月26日 17:46:13jceasetsuperseder: Instance methods and WeakRefs don't mix. ->
2012年11月26日 17:46:13jceaunlinkissue14631 superseder
2012年11月26日 17:44:33jcealinkissue813299 superseder
2012年11月26日 17:43:26jceasetsuperseder: Instance methods and WeakRefs don't mix.
2012年11月26日 17:43:26jcealinkissue14631 superseder
2012年11月26日 17:38:48jceasetnosy: + jcea
2012年11月17日 18:01:46pitrousetstatus: open -> closed
resolution: fixed
messages: + msg175793

stage: patch review -> resolved
2012年11月17日 18:00:00python-devsetnosy: + python-dev
messages: + msg175792
2012年11月12日 21:34:38pitrousetmessages: + msg175475
2012年11月12日 21:32:29mark.dickinsonsetmessages: + msg175474
2012年11月11日 21:34:09sfeltmansetmessages: + msg175392
2012年11月11日 21:23:49sfeltmansetmessages: + msg175391
2012年11月11日 20:55:06daniel.urbansetnosy: + daniel.urban
2012年11月11日 19:07:29pitrousetfiles: + weakmethod.patch
keywords: + patch
messages: + msg175384
2012年11月11日 17:16:43pitrousetnosy: + pitrou
messages: + msg175372
2012年11月11日 10:28:36mark.dickinsonsetversions: + Python 3.4, - Python 3.3
2012年11月11日 10:20:17sfeltmansetmessages: + msg175344
2012年11月11日 10:19:04mark.dickinsonlinkissue16452 superseder
2012年11月11日 10:18:49mark.dickinsonsetnosy: + sfeltman
messages: + msg175343
2012年04月20日 15:16:21mark.dickinsonsetversions: + Python 3.3, - Python 2.7
nosy: + mark.dickinson

messages: + msg158834

type: behavior -> enhancement
stage: patch review
2012年04月20日 13:49:42Sundancecreate

AltStyle によって変換されたページ (->オリジナル) /