我观察到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 docs,实例是 uncollectable !
答案 0 :(得分:4)
额外引用隐藏在f.b
中存储的绑定方法中。
print(f.b.__self__) # <__main__.Foo object at 0x000001CD147FEF98>
存储self.b = self.bar
时,可以创建绑定方法。绑定方法跟踪绑定的实例。这是允许它隐式传递self
的原因。
如果您要手动创建绑定方法,也会发生同样的情况。
import sys
class Foo:
def bar(self):
pass
f = Foo()
print(sys.getrefcount(f) - 1) # 1
bound_method = f.bar
print(sys.getrefcount(f) - 1) # 2
所以你是对的:实例化绑定方法会创建引用循环f -> f.b -> f
。虽然这不是问题,因为Python处理自version 2.0以来的循环引用。