对象可以跟踪其引用吗?

时间:2017-04-10 16:03:51

标签: python python-3.x garbage-collection

我正在研究python3。我遇到了一个奇怪的情况,只有当一个对象有办法判断是否有任何引用时才能解释。但是,这种行为似乎不会影响python2.7。我已经放弃了它作为垃圾收集问题的解释,因为gc.disable()没有任何区别。只是为了澄清,这对我来说不是问题,但我真的很想知道到底发生了什么。

如果您对特定情况感兴趣,可以使用MWE。

我正在处理从matplotlib docs剪贴的代码(matplotlib 2.0.0):

import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime

def update_title(axes):
    axes.set_title(datetime.now())
    axes.figure.canvas.draw()

fig, ax = plt.subplots()

x = np.linspace(-3, 3)
ax.plot(x, x*x)
timer = fig.canvas.new_timer(interval=100)
timer.add_callback(update_title, ax)
timer.start()
#timer_another_ref = timer
#timer = None
plt.show()

现在,如果我取消注释行timer = None,则计时器不再起作用(仅在python3中,正如我之前所说)。另一方面,如果我现在取消注释另一行,它会按预期工作。

作为旁注,我有两台电脑。一个是在ubuntu 14.04上运行python 3.4.3并使用后端Qt5Agg,另一个是在ubuntu 16.04上运行python 3.5.2并使用后端TkAgg。这种行为只能在前者上观察到(我猜它与后端有关)。

2 个答案:

答案 0 :(得分:2)

如果要获取对象的引用计数,则可以使用:

sys.getrefcount(something)

请注意,以下内容会输出2,因为它是对列表的临时引用。

lst = [1, 2, 3]
print(sys.getrefcount(lst))

答案 1 :(得分:1)

啊,它(几乎可以肯定)是由于垃圾收集。在CPython中,大量的垃圾收集是通过引用计数完成的,这与gc模块无关。 gc只需要在参考周期中收集垃圾,在这种情况下,单独引用计数是无能为力的。例如:

class T:
    def __del__(self):
        print("going away")

t = T()
print("before")
t = None  # remove only reference to the object
print("after")

在每个发布的CPython版本(即使是gc之前存在的版本)下,打印出来:

before
going away
after

现在变得更加漂亮:

x = T()
y = T()
x.ref = y
y.ref = x
print("before")
x, y = None, None
print("after")

import gc
gc.collect()
print("after gc")

x代表y,反之亦然。这是“一个循环”。每个都可以从另一个访问,因此他们的引用计数永远不会低于1(除非x.ref和/或y.ref也反弹)。因此,在所有版本的CPython下,打印出来:

before
after

起初。在Python 3下,它继续打印:

going away
going away
after gc

但在Python 2下它只会继续打印:

after gc

这是由于使用__del__方法的循环垃圾包含对象的模糊技术问题; Python 2无法回收它们,但Python 3可以。

唉,我不可能完全猜出你的例子中发生了什么。这需要在两个实现下研究fig.canvas.new_timer的源代码。

但是底线很清楚:如果你需要一个对象来保持活着,请确保它可以从你的代码中获得。在无法访问之后,一个垃圾收集子系统或其他垃圾收集系统随时可以自由销毁它。