Python创建了对类的冗余引用

时间:2018-06-14 15:39:16

标签: python python-2.7 garbage-collection

我观察到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

1 个答案:

答案 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以来的循环引用。