首页 > 解决方案 > 基于引用计数以“错误”顺序调用 Python 析构函数

问题描述

据我了解,当对象的引用计数达到 0 时,应该调用 Python 析构函数。但这种假设似乎不正确。看下面的代码:

class A:
    def __init__(self, b):
        self.__b = b
        print("Construct A")
        
    def __del__(self):
        # It seems that the destructor of B is called here.
        print("Delete A")
        # But it should be called here
        
class B:
    def __init__(self):
        print("Construct B")
        
    def __del__(self):
        print("Delete B")
        
b = B()
a = A(b)

输出

Construct B                                                                                                                                                                                                                                   
Construct A                                                                                                                                                                                                                                   
Delete B                                                                                                                                                                                                                                      
Delete A

但是 A 引用了 B,所以我希望得到以下输出:

Construct B                                                                                                                                                                                                                                   
Construct A                                                                                                                                                                                                                                   
Delete A                                                                                                                                                                                                                                      
Delete B

我没有得到什么?

标签: pythonpython-3.xdestructor

解决方案


因此,由于在解释器关闭时对象仍然存在,因此您实际上甚至不能保证__del__会被调用。此时,该语言不保证何时调用终结器。

从文档:

不能保证__del__()为解释器退出时仍然存在的对象调用方法。

请注意,如果您将脚本更改为:

(py38) 173-11-109-137-SFBA:~ juan$ cat test.py
class A:
    def __init__(self, b):
        self.__b = b
        print("Construct A")

    def __del__(self):
        # It seems that the destructor of B is called here.
        print("Delete A")
        # But it should be called here

class B:
    def __init__(self):
        print("Construct B")

    def __del__(self):
        print("Delete B")
b = B()
a = A(b)

del a
del b

然后,执行:

(py38) 173-11-109-137-SFBA:~ juan$ python test.py
Construct B
Construct A
Delete A
Delete B

虽然del不会删除对象,它会删除引用,因此它会在解释器仍在运行时强制引用计数达到 0,因此顺序如您所愿。

有时,__del__根本不会被调用。一个常见的情况是文件对象由

f = open('test.txt')

在全局范围内具有实时引用。如果没有明确关闭,它可能不会调用__del__并且文件不会刷新并且您不会得到任何写入。这是使用文件对象作为上下文管理器的一个很好的理由......


推荐阅读