简体   繁体   English

如果我在Python中引用了一个绑定方法,那么单独保持对象存活吗?

[英]If I have a reference to a bound method in Python, will that alone keep the object alive?

I wrote something like this today (not unlike the mpl_connect documentation: 我今天写了这样的东西(与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()

This looks reasonable, but it doesn't work -- it's as though matplotlib loses track of the function I've given it. 这看起来很合理,但它不起作用 - 就好像matplotlib失去了我给它的功能。 If instead of passing it Foo().callback I pass it lambda e: Foo().callback(e) , it works. 如果不是传递它Foo().callback我传递它lambda e: Foo().callback(e) ,它的工作原理。 Similarly if I say x = Foo() and then pass it x.callback , it works. 同样,如果我说x = Foo()然后传递给它x.callback ,它就可以了。

My presumption is that the unnamed Foo instance created by Foo() is immediately destroyed after the mpl_connect line -- that matplotlib having the Foo.callback reference doesn't keep the Foo alive. 我的假设是由Foo()创建的未命名的Foo实例在mpl_connect行之后立即被销毁 - 具有Foo.callback引用的matplotlib不会使Foo保持活动状态。 Is that correct? 那是对的吗?

In the non-toy code I encountered this in, the solution of x = Foo() didn't work, presumably because in that case show() was elsewhere so x had gone out of scope. 在我遇到的非玩具代码中, x = Foo()的解决方案不起作用,大概是因为在那种情况下show()在别处,所以x已超出范围。

More generally, Foo().callback is a <bound method Foo.callback of <__main__.Foo object at 0x03B37890>> . 更一般地说, Foo().callback<bound method Foo.callback of <__main__.Foo object at 0x03B37890>> My primary surprise is that it seems like a bound method isn't actually keeping a reference to the object. 我的主要惊喜是,似乎绑定方法实际上并没有保持对对象的引用。 Is that correct? 那是对的吗?

Yes, a bound method references the object - the object is the value of a bound method object's .im_self attribute. 是的,绑定方法引用该对象 - 该对象是绑定方法对象的.im_self属性的值。

So I'm wondering whether matplotlib 's mpl_connect() is remembering to increment the reference counts on the arguments passed to it. 所以我想知道matplotlibmpl_connect()是否记得增加传递给它的参数的引用计数。 If not (and this is a common error), then there's nothing to keep the anonymous Foo().callback alive when mpl_connect() returns. 如果不是(这是一个常见错误),那么当mpl_connect()返回时,没有任何东西可以保持匿名Foo().callback alive。

If you have easy access to the source code, take a look at the mpl_connect() implementation? 如果您可以轻松访问源代码,请查看mpl_connect()实现? You want to see C code doing Py_INCREF() ;-) 你想看到C代码做Py_INCREF() ;-)

EDIT This looks relevant, from docs here : 编辑这看起来很相关, 来自这里的文档

The canvas retains only weak references to the callbacks. 画布仅保留对回调的弱引用。 Therefore if a callback is a method of a class instance, you need to retain a reference to that instance. 因此,如果回调是类实例的方法,则需要保留对该实例的引用。 Otherwise the instance will be garbage- collected and the callback will vanish. 否则,实例将被垃圾收集并且回调将消失。

So it's your fault - LOL ;-) 所以这是你的错 - LOL ;-)

Here's the justification from matplotlib.cbook.CallbackRegistry.__doc__ : 这是来自matplotlib.cbook.CallbackRegistry.__doc__的理由:

In practice, one should always disconnect all callbacks when they are no longer needed to avoid dangling references (and thus memory leaks). 实际上,当不再需要它们时,应该始终断开所有回调以避免悬空引用(从而避免内存泄漏)。 However, real code in matplotlib rarely does so, and due to its design, it is rather difficult to place this kind of code. 但是,matplotlib中的实际代码很少这样做,并且由于其设计,放置这种代码相当困难。 To get around this, and prevent this class of memory leaks, we instead store weak references to bound methods only, so when the destination object needs to die, the CallbackRegistry won't keep it alive. 为了解决这个问题,并防止这类内存泄漏,我们只将弱引用存储到绑定方法,因此当目标对象需要死亡时,CallbackRegistry将不会保持活动状态。 The Python stdlib weakref module can not create weak references to bound methods directly, so we need to create a proxy object to handle weak references to bound methods (or regular free functions). Python stdlib weakref模块无法直接创建对绑定方法的弱引用,因此我们需要创建一个代理对象来处理对绑定方法(或常规自由函数)的弱引用。 This technique was shared by Peter Parente on his "Mindtrove" blog <http://mindtrove.info/articles/python-weak-references/> _. Peter Parente在他的"Mindtrove" blog <http://mindtrove.info/articles/python-weak-references/> _上分享了这项技术。

It's a shame there isn't an official way to bypass this behavior. 遗憾的是,没有一种官方方法可以绕过这种行为。

Here's a kludge to get around it, which is dirty but may be OK for non-production test/diagnostic code: attach the Foo the the figure: 这是一个解决它的问题,这很脏但是对于非生产测试/诊断代码可能没问题:将Foo附加到图中:

fig._dont_forget_this = Foo()
cid = fig.canvas.mpl_connect('button_press_event', fig._dont_forget_this.callback)

This still leaves the question of why lambda e: Foo().callback(e) works. 这仍然存在为什么lambda e: Foo().callback(e)有效的问题。 It obviously makes a new Foo at every call, but why doesn't the lambda get garbage collected? 它显然在每次调用时都会生成一个新的Foo ,但为什么lambda不会收集垃圾? Is the fact that it works just a case of undefined behavior? 它只是一个未定义行为的事实吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM