【发布时间】:2018-06-14 15:39:15
【问题描述】:
我观察到 Python 2.7.12 和 Python 3.5.2 的一个奇怪行为:
import sys
class Foo:
def __init__(self):
self.b = self.bar
def bar(self):
pass
f = Foo()
print(sys.getrefcount(f) - 1) # subtract the extra reference created by
# passing a reference to sys.getrefcount.
当我在 python 中运行代码时,我得到 2,这意味着有两个对 Foo 对象的引用。唯一的解决方案是删除self.b = self.bar。它似乎创建了一个循环引用。
谁能解释一下这种行为?
更新: 以下是运行结果:
class Foo:
def __init__(self):
self.b = self.bar
def bar(self):
pass
def __del__(self):
print "deleted"
def test_function():
f = Foo()
print f
if __name__ == "__main__":
for i in xrange(5):
test_function()
print "finished"
python ./test.py
<__main__.Foo instance at 0x104797e60>
<__main__.Foo instance at 0x104797ef0>
<__main__.Foo instance at 0x104797f38>
<__main__.Foo instance at 0x104797f80>
<__main__.Foo instance at 0x104797fc8>
finished
正如您在每次迭代中看到的那样,python 会创建 Foo 类的新实例,但永远不会释放它们!
更新 2 和根本原因
具有 del() 方法且属于引用循环的对象 导致整个参考周期无法收集,包括 对象不一定在循环中,但只能从循环中到达。 https://docs.python.org/2/library/gc.html#gc.garbage
Foo 类实例同时具有__del__ 方法和绑定方法self.b = self.bar,这意味着它绝对是引用循环的一部分。
因此,根据 py 文档,该实例是 uncollectable!
【问题讨论】:
-
你是说 Foo 永远不会被收集吗?你能提供代码来证明是这种情况吗?仅仅打印引用计数并不能令人信服,因为引用计数非零的对象仍然可以被收集。
-
@Kevin,请在更新中找到结果。
-
@fsqirrel 很好
__del__函数不会删除,它必须调用 super().__del__ 或者你只是做 del 约,不是 100% 肯定但值得一试 -
感谢您发布您的结果。当我在 2.7 中运行您的代码时,我得到与您相同的输出,但是当我将其移植到 3.X(将打印语句更改为函数,并将 xrange 更改为范围)时,“完成”后出现“已删除”消息。不过,即使在 3.X 中,我也不会依赖这种情况,因为文档说“不能保证 __del__() 方法会在解释器退出时为仍然存在的对象调用。”我本来建议在程序结束之前手动调用
gc.collect(),但奇怪的是,这似乎并没有影响 2.7 中的结果。
标签: python python-2.7 garbage-collection