为什么我不能得到预期的输出'BBB'?

2024-03-28 20:30:43 发布

您现在位置:Python中文网/ 问答频道 /正文

# -*- coding: utf-8 -*-
class tA():
    def __init__(self):
        print 'AAA'

    def __del__(self):
        print 'BBB'

class tC(tA):
    def __init__(self, a, b=0):
        tA.__init__(self)
        self.b=b             # 1. del this ok

class tD(tC):
    def __init__(self):
        a=1
        #tC.__init__(self, a)             # 2. use this ok
        tC.__init__(self, a, self.func)   # 3. use this not ok
        #tC.__init__(self, a, 3)          # 4. use this ok

    def func(self, pos):
        pass
if __name__ == '__main__':
    tD()

为什么没有“BBB”输出?你知道吗

如果我del zhe#1,输出是正常的

如果使用#2或#4,则输出正常

如果使用#3,则输出没有“BBB”?你知道吗


Tags: selfinitusedefokthisclasstd
2条回答

因为'BBB'是用类的终结器(__del__函数)打印的。垃圾收集器收集对象时,将运行终结器和终结器。你知道吗

Python对垃圾收集使用双重策略:引用计数和循环检测。引用计数达到0的对象将被立即收集,但如果它们参与循环,则它们的计数将永远不会达到0。然后定期调用GC循环检测例程,最终将检测到它并释放所有悬空对象。你知道吗

在您的特定代码中,case#3创建了一个引用循环:self.b是对self.func的引用。但是GC循环检测永远不会运行,因为程序在有机会之前就结束了。你知道吗

但是,即使GC运行,带有终结器的对象也有特殊的规则。从documentation

Objects that have __del__() methods and are part of a reference cycle cause the entire reference cycle to be uncollectable, including objects not necessarily in the cycle but reachable only from it. Python doesn’t collect such cycles automatically because, in general, it isn’t possible for Python to guess a safe order in which to run the __del__() methods.

另外,从here

Changed in version 3.4: Following PEP 442, objects with a __del__() method don’t end up in gc.garbage anymore.

因此,在3.4之前的Python中,在带有终结器的类中,您必须手动中断周期:

If you know a safe order, you can force the issue by examining the garbage list, and explicitly breaking cycles due to your objects within the list. Note that these objects are kept alive even so by virtue of being in the garbage list, so they should be removed from garbage too. For example, after breaking cycles, do del gc.garbage[:] to empty the list.

因为func是一个绑定方法,因此间接引用它绑定到的对象,这意味着您正在创建一个引用循环。你知道吗

您可以通过执行以下操作来验证这一点:

...

if __name__ == '__main__':
    import sys
    print(sys.getrefcount(tD()))

在您的案例2和案例4中应该打印1,在案例3中应该打印2。你知道吗

^{}的文档中有一个关于引用循环的注释:

[...] Some common situations that may prevent the reference count of an object from going to zero include: circular references between objects (e.g., a doubly-linked list or a tree data structure with parent and child pointers); a reference to the object on the stack frame of a function that caught an exception (the traceback stored in sys.exc_traceback keeps the stack frame alive); or a reference to the object on the stack frame that raised an unhandled exception in interactive mode (the traceback stored in sys.last_traceback keeps the stack frame alive)
[...]
Circular references which are garbage are detected when the option cycle detector is enabled (it’s on by default), but can only be cleaned up if there are no Python-level __del__() methods involved.

基本上,这意味着如果您有一个__del__方法,它将阻止清理包含引用循环的对象。你知道吗

相关问题 更多 >