Cancel

Python Cleanup

Language

·

October 16, 2024

截止于 Python 3.13

前言

Python 资源自动释放的机制就是四种:with, __del__, atexit , weakref.finalize

with

适用于局部资源管理,是主流的资源自动释放手段,还有方便编写的 contextlib.contextmanager 装饰器。

但是对于经典的单例、全局资源类不适用。

__del__

即编写对象的钩子函数 __del__ ,但是 Python 没有为它提供任何有力的保证,语义实现的机制非常脆弱,是一种早期失败的设计。

它有以下几个特点:

苏生 UB

在方法内部创建新的对象,从而为它的指针计数器加一,这个做法称为苏生(resurrection),但当这个新的对象被销毁时 __del__ 是否会被调用是 Implement Dependent 。

非确定性执行

不保证解释器退出的时候会被执行。

用户负责

用户代码里注意不要申请可能会造成死锁的资源。

用户自己注意不要引用可能已被销毁的全局变量。1

结论

总之就是失败设计,不如不不用。

atexit

确定在解释器退出时会被调用,是静态生命周期、全局资源类选项。

atexit.register(Callable[[], Any])

没有提供针对资源类的方法装饰器,只能在 __init___ 里手动加上一行类似 register(self.cleanup) 的代码。

而注册了类方法之后,相当于让这个资源类永远有一个引用在那里,导致它永远不会被回收,相当于把资源类的生命周期提高到了 static 。

weakref.finalize

作为 __del__ 的上位替代,在资源回收(gc)的时候调用,解决了 __del__ 的在解释器退出时不确定执行的问题,但几乎也就仅此而已。

尾声

从编程角度,资源管理只有两个模式,一个是在局部上下文里使用-释放;另一个是使用全局单例模式。

前者是最推荐的设计模式, with 机制已经处理很好了;而后者里是设计上的反模式,是传统上的 tradeoff 的做法,需要自己实现一个机制。

这样全局资源类的资源释放,实际上应该在单例模式的机制里顺便实现。

如下展示如何通过标记 cleanup 方法和使用 atexit.register ,在一个单例模式里实现资源自动释放。

注解

  1. Python guarantees that globals whose name begins with a single underscore are deleted from their module before other globals are deleted. ↩

© minghu6

·

theme

Simplex theme logo

by golas