如果我在Python中引用了绑定方法,那么仅此一点就可以使对象保持活动状态吗?

如果我在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

我今天写了类似的东西(与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()
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)