如果我在Python中引用了绑定方法,那么仅此一点就可以使对象保持活动状态吗?
我今天写了类似的东西(与mpl_connect文档不同:如果我在Python中引用了绑定方法,那么仅此一点就可以使对象保持活动状态吗?,python,matplotlib,Python,Matplotlib,我今天写了类似的东西(与mpl_connect文档不同: class Foo(object): def __init__(self): print 'init Foo', self def __del__(self): print 'del Foo', self def callback(self, event=None): print 'Foo.callback', self, event from pylab import * fig = figure() plo
class Foo(object):
def __init__(self): print 'init Foo', self
def __del__(self): print 'del Foo', self
def callback(self, event=None): print 'Foo.callback', self, event
from pylab import *
fig = figure()
plot(randn(10))
cid = fig.canvas.mpl_connect('button_press_event', Foo().callback)
show()
这看起来很合理,但不起作用——就像matplotlib失去了我给它的函数的轨迹。如果不是传递它Foo().callback
而是传递它lambda e:Foo().callback(e)
,它就起作用了。同样,如果我说x=Foo()
,然后传递它x.callback
,它就起作用了
我的假设是,由Foo()
创建的未命名Foo实例在mpl\u connect
行之后立即被销毁——具有Foo.callback
引用的matplotlib不会使Foo
保持活动状态。这是正确的吗
在我在中遇到的非玩具代码中,x=Foo()
的解决方案不起作用,可能是因为在这种情况下,show()
在别处,所以x
超出了范围
更一般地说,
Foo().callback
是一个
。我最惊讶的是,绑定方法似乎没有实际保留对对象的引用。这是否正确?是的,绑定方法引用对象-对象是绑定方法对象的.im\u self
属性的值
所以我想知道matplotlib
的mpl_connect()
是否记得增加传递给它的参数的引用计数。如果没有(这是一个常见错误),那么当mpl_connect()
返回时,匿名Foo()
如果您可以轻松访问源代码,请查看mpl\u connect()
实现?您希望看到C代码执行Py\u INCREF()
;-)
编辑这看起来很相关:
画布只保留对回调的弱引用。所以
如果回调是类实例的方法,则需要保留
对该实例的引用。否则实例将是垃圾-
已收集,回调将消失
所以这是你的过错(哈哈) 以下是来自matplotlib.cbook.CallbackRegistry.\uuuu doc\uuuu
的理由:
在实践中,在调用时应该始终断开所有回调
不再需要它们来避免挂起引用(从而避免内存)
泄漏)。然而,matplotlib中的实际代码很少这样做,而且由于
就其设计而言,放置此类代码相当困难。
为了避免这种情况,并防止此类内存泄漏,我们
相反,只存储对绑定方法的弱引用,因此
目标对象需要终止,CallbackRegistry将无法保留
它活着。Python stdlib weakref模块无法创建弱
直接引用绑定方法,所以我们需要创建一个代理
对象来处理对绑定方法(或常规自由方法)的弱引用
功能)。Peter Parente在他的博客上分享了这一技巧
“Mindtrove”博客
\
遗憾的是,没有一个官方的方法可以绕过这种行为
这里有一个乱七八糟的地方可以绕过它,它很脏,但对于非生产测试/诊断代码可能还可以:将Foo
fig._dont_forget_this = Foo()
cid = fig.canvas.mpl_connect('button_press_event', fig._dont_forget_this.callback)
这仍然留下了一个问题:为什么lambda e:Foo().callback(e)
有效。显然,它在每次调用时都会生成一个新的Foo
,但是为什么lambda没有被垃圾收集呢?它工作的事实只是一个未定义行为的例子吗?是否打印过'del Foo'
?mpl\u connect
的figureCasbase
使用weakref.WeakKeyDictionary
和weakref.ref
@falsetru,是的,刚刚编辑过,以显示文档甚至这么说。谢谢。你是对的。我跳到了他们给出的例子,但是当调用show
超出范围时,我不知道他们为什么做出这样的设计决定。我不认为什么时候这种行为比持有强引用更可取。你会认为至少mpl\u connect
可以有一个strongref
参数,这样你就可以说fig.canvas.mpl\u connect('button\u press\u event',strongref=Foo().callback)