5

在 Python 中,我可以使用atexit模块来注册 Python 退出时要执行的函数。有没有办法检索已注册退出处理程序的列表?

4

5 回答 5

9

这是一种访问已注册函数(可调用对象)的纯 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__然后复制它的论点。

于 2020-07-22T07:50:05.713 回答
7

在 Python 2 中,该模块仍然仅作为 Python 可用,该atexit模块有一个半私有列表:

atexit._exithandlers

它包含所有已注册的退出处理程序。

在 Python 3 中,该模块已经用 C 重新编码,并且列表不再可访问,所以对于 Python 3,您恐怕完全不走运。

您必须将Python 2 纯 Python 版本移植到 Python 3,并确保使用它而不是 C 版本以使列表再次可访问。

于 2013-04-16T16:42:01.953 回答
4

这是@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
于 2020-09-09T14:18:17.320 回答
2

在 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
于 2017-12-16T19:12:33.633 回答
1

有一个公开的功能请求来公开此信息(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
于 2019-07-04T18:59:59.440 回答