You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何将实例存入装饰器类时__del__方法未被调用?如何修复?

问题原因与解决方案

问题原因

当你将实例添加到装饰器的_instances列表时,列表持有对该实例的强引用。Python的垃圾回收机制仅在对象的引用计数降为0时,才会触发__del__方法并回收对象。由于装饰器的列表仍保留着实例的引用,即使你删除了my_a这类变量,对象的引用计数仍大于0,因此__del__不会被调用,计数也无法正确更新。

解决方案

使用Python标准库中的weakref.WeakSet存储实例。WeakSet会自动持有对象的弱引用,不会阻止对象被垃圾回收。当实例的最后一个强引用被删除时,垃圾回收器会回收该实例,自动将其从WeakSet中移除,同时触发实例的__del__方法。

修正后的代码

import weakref
import gc  # 用于强制触发垃圾回收,仅测试场景需要

class InstanceCounter:
    def __init__(self, cls):
        self._instances = weakref.WeakSet()
        self._wrapped_cls = cls

    @property
    def instance_count(self):
        return len(self._instances)

    def __call__(self):
        obj = self._wrapped_cls()
        self._instances.add(obj)
        return obj


@InstanceCounter
class MyClass:
    def __init__(self):
        pass

    def __del__(self):
        print(f"Deleting object of class {__class__}")


@InstanceCounter
class OtherClass:
    def __init__(self):
        pass

# 测试用例
my_a = MyClass()
print(f"{MyClass.instance_count=}")  # MyClass.instance_count=1

my_b = MyClass()
print(f"{MyClass.instance_count=}")  # MyClass.instance_count=2

del my_a
gc.collect()  # 强制触发垃圾回收,确保实例立即被回收
print(f"{MyClass.instance_count=}")  # MyClass.instance_count=1

oth_c = OtherClass()
print(f"{OtherClass.instance_count=}")  # OtherClass.instance_count=1

oth_d = OtherClass()
print(f"{OtherClass.instance_count=}")  # OtherClass.instance_count=2

del oth_c
gc.collect()
print(f"{OtherClass.instance_count=}")  # OtherClass.instance_count=1

del oth_d
gc.collect()
print(f"{OtherClass.instance_count=}")  # OtherClass.instance_count=0

关键修改说明

  1. 替换为WeakSet:将_instances从列表改为weakref.WeakSet,避免持有强引用导致实例无法被回收。
  2. 移除手动计数逻辑:不再需要_count变量和__del__方法的装饰器,instance_count直接返回WeakSet的长度,自动反映存活实例数量。
  3. 强制垃圾回收(测试用):在删除实例后调用gc.collect(),确保垃圾回收立即触发,测试时能看到计数实时更新。生产环境中通常无需手动调用,Python会自动管理垃圾回收。

内容的提问来源于stack exchange,提问作者Jan-Willem Lankhaar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 23:45:33