Python 垃圾收集和单例 类

Python garbage collection and singletons classes

我有一个单例 class,我不明白 Python 垃圾收集器为什么不删除实例。

我正在使用 - from singleton_decorator import singleton

我的例子 class:

from singleton_decorator import singleton

@singleton
class FilesRetriever:

    def __init__(self, testing_mode: bool = False):
        self.testing_mode = testing_mode

测试示例:

def test_singletone(self):
    FilesRetriever(testing_mode=True)
    mode = FilesRetriever().testing_mode
    print("mode 1:" + str(mode))
    mode = FilesRetriever().testing_mode
    print("mode 2:" + str(mode))
    count_before = gc.get_count()
    gc.collect()
    count_after = gc.get_count()
    mode = FilesRetriever().testing_mode
    print("mode 3:" + str(mode))
    print("count_before:" + str(count_before))
    print("count_after:" + str(count_after))   

测试输出:

mode 1:True
mode 2:True
mode 3:True
count_before:(306, 10, 5)
count_after:(0, 0, 0)

我希望在垃圾收集器自动运行之后或在我的测试中 运行 _SingletonWrapper 的实例(装饰器实现中的 class )将被删除,因为没有任何指向给它。然后 "print("mode 3:" + str(mode))" 的值将为 False 因为这是默认值(并且实例已重新创建)

所以代码和垃圾回收工作正常。查看您所指的单例装饰器的代码。 decorator

仅仅因为您调用 gc.collect() 并且您的代码没有在某处持有引用并不意味着其他代码没有。

在装饰器中,它创建一个实例,然后将该实例存储在装饰器内的一个变量中。所以即使你收集了相对于你的代码。他们的代码仍然持有对该实例的引用,因此不会被收集。

这是单身​​人士的预期行为,因为这是它的全部目的。就是将实例存储在某个地方,可以检索和使用,而不是每次都创建一个新实例。因此,除非您需要更换实例,否则您不希望该实例被丢弃。

回复您的评论

  1. 不,您没有将实例获取到 _SingletonWrapper。当您编写 FileRetriever() 时,您实际上是在调用 _SingletonWrapper 实例的 __call__ 方法。当您使用 @singleton() 时,returns 不是 class 对象的实例。

  2. 同样,虽然在您的代码中您没有将它存储在任何地方并不意味着它没有存储在其他地方。当您定义一个 class 时,您在某种意义上所做的是在模块的全局范围内,它正在创建一个包含该 class 定义的变量。所以在你的全球范围内的代码中它有这样的东西,

FileRetriever = (class:
    def __init__(self):
        blahblahblah

现在您的 class 定义存储在变量调用 FileRetriever 中。

所以现在你正在使用装饰器,所以现在它看起来像这样基于单个装饰器中的代码。

FileRetriever = _SingletonWrapper(class: blahblah)

现在您 class 被包装并存储在变量 FileRetriever 中。 现在您在 运行 FileRetriever() 时调用 _SingletonWrapper.__call__()。 因为 __call__ 是一个实例方法。它可以包含对您的原始 class 的引用和您声明的 class 的实例,因此即使您删除了所有您对 class 的引用,此代码仍然保留参考。

如果你真的想删除所有对你单身的引用,我不确定你为什么要这样做。您需要删除对包装器的所有引用以及您 class。所以像 FileRetriever = None 这样的东西可能会导致 gc 收集它。但是在这个过程中你会失去你原来的 class 定义。