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




