atexit --- 退出处理器


atexit 模块定义了用于注册和取消注册 退出处理器 的函数:即在“退出时”自动执行的函数,也就是说,当程序正常终结(例如,sys.exit() 被调用或主模块执行结束)时或者更一般的情况,当 interpreter shutdown 时。

退出时,所有已注册的退出处理器都会按照其注册时的 相反 顺序被调用。 如果你注册了 A, B 和 C,则在解释器关闭时它们将按照 C, B, A 的顺序运行。 这样做的假设是较低层级的模块通常会在较高层级的模块之前被导入,因此必须在较晚的时候被清理。

如果在退出处理器执行期间引发了异常,将会打印回溯信息(除非引发的是 SystemExit )并且异常信息会被保存。 在所有退出处理器都运行完毕之后,所引发的最后一个异常会被重新引发。

在使用多个解释器的程序中,每个解释器都有自己的退出处理器栈,它们会在该解释器关闭时执行(例如通过 C API 函数 Py_EndInterpreter() )。 此模块中的注册函数仅影响调用它们的解释器。

备注: 当程序被未由 Python 处理的信号终止时、检测到 Python 内部致命错误时,或者调用了 os._exit() 时,退出处理器将不会被调用。

注意: 在清理函数内部注册或注销函数可能产生的影响是未定义的。

警告

在编写退出处理器时,特别是在 C API 扩展中,请记住在完成清理之后其他退出处理器仍可能运行任意 Python 代码。 此类代码应当成功或因异常而失败,而不是导致程序崩溃。

在 3.12 版本发生变更: 现在尝试在退出处理器中启动新线程或通过 os.fork() 派生新进程会导致 RuntimeError。 在此之前,这可能导致主 Python 运行时线程在释放线程状态的同时内部 threading 例程或新进程尝试使用该状态之间的竞争条件,进而可能导致程序崩溃而不是正常关闭。

在 3.7 版本发生变更: 当配合子解释器使用时,已注册函数仅作用于它们被注册的解释器。

atexit.register(func, *args, **kwargs)

将 func 注册为退出处理器。 任何要传递给 func 的可选参数都必须作为参数传递给 register()。 可以多次注册相同的函数和参数。

这个函数返回 func 对象,可以把它当作装饰器使用。

atexit.unregister(func)

将 func 从退出处理器列表中移除。 如果 func 此前未被注册, unregister() 将静默地不做任何事。 如果 func 已被多次注册,该函数在 atexit 调用栈中的每一次出现都将被移除。 取消注册期间内部使用相等性比较( == ),因此函数引用无需具有匹配的标识。

参见

模块 readline

使用 atexit 读写 readline 历史文件的有用的例子。

atexit 示例

以下简单例子演示了一个模块在被导入时如何从文件初始化一个计数器,并在程序终结时自动保存计数器的更新值,此操作不依赖于应用在终结时对此模块进行显式调用。:

try:
    with open('counterfile') as infile:
        _count = int(infile.read())
except FileNotFoundError:
    _count = 0

def incrcounter(n):
    global _count
    _count = _count + n

def savecounter():
    with open('counterfile', 'w') as outfile:
        outfile.write('%d' % _count)

import atexit

atexit.register(savecounter)

位置和关键字参数也可传入 register() 以便传递给被调用的已注册函数:

def goodbye(name, adjective):
    print('Goodbye %s, it was %s to meet you.' % (name, adjective))

import atexit

atexit.register(goodbye, 'Donny', 'nice')
# 或者:
atexit.register(goodbye, adjective='nice', name='Donny')

作为 decorator: 使用:

import atexit

@atexit.register
def goodbye():
    print('You are now leaving the Python sector.')

只有在函数不需要任何参数调用时才能工作.