首页 > 解决方案 > 删除对象后成员变量的引用计数不减少

问题描述

背景真相(?):当python程序退出时,gcgarbage collection)以一种常见的方式收集内存reference counting

所以当一个对象被删除时,它的成员对象的引用计数应该减1。但我发现引用计数不会减1,除非del__del__函数中调用

import sys

class B():
    def __init__(self):
        self.name = 'little b construct'
    def __del__(self):
        print(sys.getrefcount(self))
        print('little b was deleted')

class A():
    def __init__(self):
        print('little a construct')
        self._b = B()

    def __del__(self):
        # del self._b   # makes B' reference minus 1
        print('little a was deleted')

if __name__ == '__main__':
    a = A()

结果:

# without del self._b
little a construct
little a was deleted
4
little b was deleted
# with del self._b
little a construct
3
little b was deleted
little a was deleted

问题:

  1. 为什么删除 A 后 B 的引用计数不会自动减 1?

  2. B的引用计数是由什么4组成的?

谢谢你的帮助

标签: pythonpython-3.xreference

解决方案


我已经简化了你的例子,以便更容易在心里计算参考。

发生的情况是,您必须计算对象的引用,方法内的 self 的引用,以及在调用时运行的任何函数调用的参数中对同一对象的其他引用sys.getrefcount,包括该函数,因为它还在自己的本地范围内保存了引用的副本。

因此,您缺少的是调用函数,需要将对象作为参数传递,使新名称绑定到本地函数/方法范围内的形式参数。总是有一个+1的偏移量,即使你只有一个指向对象的名称,正是因为这个新的本地引用 inside sys.getrefcount

import sys

class B():
    def __init__(self):
        print('setting up b...')
    def counter(self):
        return sys.getrefcount(self)

class A():
    def __init__(self):
        print('setting up a...')

if __name__ == '__main__':
    a = A()
    b = B()
    print(sys.getrefcount(a))
    print(sys.getrefcount(b))

    print(b, b.counter())  # 5 refs to b. Why 5?
    print(b, B.counter(b)) # Because this is what really happens.
    print(b.counter(), b ) # 4 refs to b

    print(b,b,b.counter()) # 6 refs to b

输出:

setting up a...
setting up b...
2
2
<__main__.B object at 0x7f11df905940> 5
4 <__main__.B object at 0x7f11df905940>
<__main__.B object at 0x7f11df905940> <__main__.B object at 0x7f11df905940> 6

除此之外,这__init__不是构造函数,那__new__是您通常不需要的,__init__设置已创建并可通过 访问的对象self,请参阅我的另一个答案here__del__通常也不需要,除非您需要一些特殊的资源清理只是在对象销毁之前(不需要销毁包含的对象)。

最后 del不会删除一个对象,它只是删除一个绑定到它的名称;它会减少 refcount,并且在 refcount 达到 0 后,它最终会被 GC 收集。

我对 Q1 的解释是它a仍然存在。__del__正如您在文档的评论链接中提到的,__del__终结器不是析构函数。Refcount 是0意味着__del__被调用,并不意味着对象被销毁,__del__也不会销毁它。因此,您打印的消息不准确(到破坏时刻)。它会被 GC 销毁,但你不知道确切的时间(语言中没有指定,取决于实现)。

实验替换语句

a = A()

只是

A()

然后不保留对 A() 对象的引用,这也会将您的引用计数减少b一(同时保持输出的其余部分相同),这意味着不再有a._b. 所以应该是a内存中的存在a._b解释了这个 4 而不是 3.a在运行时仍然存在B.__del__del a如果您改为添加 a作为最后一条语句,也会发生同样的情况(b 的计数减少) 。


推荐阅读