如何在Python3中获取已注册的atexit函数列表?

时间:2013-04-16 16:38:53

标签: python python-3.x

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

5 个答案:

答案 0 :(得分:5)

在Python 2中,模块仍然只能作为Python使用,atexit模块有一个半私有列表:

atexit._exithandlers

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

在Python 3中,该模块已经在C中重新编码,并且该列表不再可访问,因此对于Python 3而言,恐怕是运气不好。

您必须将Python 2 pure-python version移植到Python 3,并确保使用它而不是C版本来重新访问列表。

答案 1 :(得分:4)

这是访问已注册函数(可调用对象)的纯Python方法,但不是用于调用它们的参数的方法。有点麻烦,但是对于调试之类的事情,它就可以了:

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进行相等性比较来实现。这将导致呼叫c.__eq__(other),其中other是回调(永远不会跳过此呼叫,反之则引发NotImplemented)。 Capture.__eq__然后复制其参数。

答案 2 :(得分:2)

这是@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

答案 3 :(得分:1)

在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

答案 4 :(得分:0)

有一个公开功能要求公开此信息(bpo-32082)。该请求引用了Cython模块here形式的解决方法,其关键部分是:

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