在 Python 中,我可以使用atexit模块来注册 Python 退出时要执行的函数。有没有办法检索已注册退出处理程序的列表?
5 回答
这是一种访问已注册函数(可调用对象)的纯 python 方式,但不是它们将被调用的参数。这有点 hack,但对于调试等,它会做得很好:
Python 3.5.3 (default, Jul 9 2020, 13:00:10)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import atexit
>>> class Capture:
... def __init__(self):
... self.captured = []
... def __eq__(self, other):
... self.captured.append(other)
... return False
...
>>> c = Capture()
>>> atexit.unregister(c)
>>> print(c.captured)
[<function <lambda> at 0x7fc47631d730>, <built-in function write_history_file>]
>>> atexit.unregister(c.captured[0]) # this will work
它是如何工作的:如文档所述,从回调列表atexit.unregister(c)
中删除任何出现的。c
它通过依次比较每个回调与c
是否相等来做到这一点。这导致 call c.__eq__(other)
,other
作为回调(这个调用永远不会被跳过,就像 raises 的另一种方式一样NotImplemented
)。Capture.__eq__
然后复制它的论点。
在 Python 2 中,该模块仍然仅作为 Python 可用,该atexit
模块有一个半私有列表:
atexit._exithandlers
它包含所有已注册的退出处理程序。
在 Python 3 中,该模块已经用 C 重新编码,并且列表不再可访问,所以对于 Python 3,您恐怕完全不走运。
您必须将Python 2 纯 Python 版本移植到 Python 3,并确保使用它而不是 C 版本以使列表再次可访问。
这是@BCarvello 以函数形式提供的解决方案:
import atexit
def get_atexit_functions():
funs = []
class Capture:
def __eq__(self, other):
funs.append(other)
return False
c = Capture()
atexit.unregister(c)
return funs
在 Python 3 中,atexit._exithandlers 列表不可用,但如果您只需要计算注册的回调,您可以这样做:
atexit._ncallbacks()
演示:
# python3
Python 3.5.3rc1 (default, Jan 3 2017, 04:40:57)
[GCC 6.3.0 20161229] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import atexit
>>> atexit._ncallbacks()
2
有一个公开的功能请求来公开此信息(bpo-32082)。该请求在此处引用了 Cython 模块形式的解决方法,其中关键部分是:
from cpython.ref cimport PyObject
# Internal structures defined in the CPython source in
# Modules/atexitmodule.c and subject to (but unlikely to) change. Watch
# https://bugs.python.org/issue32082 for a request to (eventually)
# re-expose more of the atexit module's internals to Python
ctypedef struct atexit_callback:
PyObject* func
PyObject* args
PyObject* kwargs
ctypedef struct atexitmodule_state:
atexit_callback** atexit_callbacks
int ncallbacks
int callback_len
cdef extern from "Python.h":
void* PyModule_GetState(object module)
def _get_exithandlers():
"""Return list of exit handlers registered with the atexit module."""
cdef atexitmodule_state* state
cdef atexit_callback callback
cdef list exithandlers
cdef int idx
cdef object kwargs
state = <atexitmodule_state*>PyModule_GetState(atexit)
if not state:
raise RuntimeError("atexit module state missing or corrupt")
exithandlers = []
for idx in range(state.ncallbacks):
callback = state.atexit_callbacks[idx][0]
if callback.kwargs:
kwargs = <object>callback.kwargs
else:
kwargs = {}
exithandlers.append((<object>callback.func,
<object>callback.args,
kwargs))
return exithandlers