You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python中异常发生后如何正确处理对象删除问题

Python中异常发生后如何正确处理对象删除问题

嗨,我来帮你搞定这个问题~

首先得搞明白为啥会出现这种情况:当你在VSCode的交互式窗口里执行foo(A())时,A()创建的实例先作为参数传给foo,但foo立刻抛出了异常。而交互式环境为了方便调试,会把异常相关的栈帧、局部对象(包括这个A实例)暂时保留下来,比如存在sys.last_traceback这类地方,导致这个对象的引用计数没降到0,所以__del__方法就不会被触发。而你手动执行a = A(); del a时,del a直接删除了唯一的引用,引用计数归零,自然就触发了对象的销毁。

接下来给你几个可行的解决办法:

方法1:用上下文管理器(with语句)控制对象生命周期

如果可以修改类的话,给它加上__enter____exit__方法,用with块来包裹对象的使用。不管块内有没有异常,with语句都会确保对象的引用被及时释放:

class A():
    def __init__(self) -> None:
        print(f"A[{id(self)}] created")
    
    def __del__(self):
        print(f"A[{id(self)}] deleted !!")
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 这里可以写自定义的清理逻辑,也可以留空让__del__自己处理
        pass

def foo(obj):
    raise ValueError

# 用with块执行,异常发生后对象也会被销毁
with A() as obj:
    foo(obj)

方法2:把代码放到局部作用域里

把创建对象和调用函数的逻辑封装到一个函数里,函数执行完(哪怕抛异常),局部作用域里的对象引用会被自动清理,引用计数归零后就会触发__del__

class A():
    def __init__(self) -> None:
        print(f"A[{id(self)}] created")
    
    def __del__(self):
        print(f"A[{id(self)}] deleted !!")

def foo(obj):
    raise ValueError

def run():
    a = A()
    foo(a)

# 调用这个函数,异常抛出后对象会被销毁
run()

方法3:手动触发垃圾回收(不推荐,应急用)

如果不想改太多代码,可以导入gc模块手动触发垃圾回收,强制清理未被引用的对象:

import gc

class A():
    def __init__(self) -> None:
        print(f"A[{id(self)}] created")
    
    def __del__(self):
        print(f"A[{id(self)}] deleted !!")

def foo(obj):
    raise ValueError

foo(A())
# 手动触发GC
gc.collect()

不过这种方法不推荐作为常规手段,因为Python的垃圾回收机制本来就是自动的,强制调用可能会影响性能,而且依赖GC的时机也不够可靠。

最后补充一句:如果是在非交互式环境(比如直接运行.py脚本)里执行foo(A()),程序结束后所有对象都会被自动清理,__del__也会触发,只有交互式环境才会因为保留调试信息而出现这个问题~

备注:内容来源于stack exchange,提问作者vuvu 700

火山引擎 最新活动