【问题标题】:Run cython code when extension module gets garbage collected当扩展模块被垃圾收集时运行 cython 代码
【发布时间】:2019-01-28 18:32:18
【问题描述】:

有没有办法在 Cython 扩展模块中注册一个函数,以便在 Python 垃圾收集器销毁模块对象时调用该函数?

类似

def __dealloc__(...)

在模块级别?

【问题讨论】:

  • 我已经添加了 cpython 和 python-internals,希望有人能说出在纯 Python 中是否有“官方”的可能性。

标签: cython cpython python-internals


【解决方案1】:

我不确定是否有明确/官方的方式来实现这一目标。但是,您可以添加一个全局变量,当 cython 模块被释放时,该变量将被销毁:

# foo.pyx:
class Observer:
    def __init__(self):
        print("module initialized")

    def __del__(self):
        print ("module deleted")

_o=Observer()

但是,通过cythonize foo.pyx -i 对其进行cythonizing 不会产生预期的效果:

[$] python -c `import foo`
module initialized

没有打印到控制台的“模块已删除”!

问题:无论如何都无法重新加载 cython 扩展(例如通过importlib.reload()),因此只有在 Python 解释器关闭时才会释放它。但是没有必要释放资源......

Cython 将所有全局 Python 变量保存在全局 C 变量中,在 cython 化的 C 源代码中称为 static PyObject *__pyx_d;;这只是一个 Python 字典。但是,由于在这个未销毁的字典中有对全局 _o 的引用,_o 的引用计数永远不会变为 0,因此对象不会被销毁。

通过设置 generate_cleanup_code=True 可以强制 Cython 生成一个清理函数,该函数将被放入 PyModuleDef 定义结构的 m_free-slot 中。例如使用以下设置文件:

from distutils.core import setup

from Cython.Build import cythonize
from Cython.Compiler import Options

Options.generate_cleanup_code = True  #  this is important!

setup(
    name = "foo",
    ext_modules = cythonize("foo.pyx"),
)

现在python setup.py build_ext -i之后:

[$] python -c "import foo"
module initialized
module deleted

它按预期工作。


因为 Cython 扩展没有 reload,所以只有在调用 module_dealloc 时才会看到“模块已删除”。

但是,当第一个列表是纯 Python 时,如果调用了 importlib.reload(foo),我们也会看到 module deleted。在这种情况下,不使用“module_dealloc”,但所有对全局 _o 的引用都将被清除,对象将被销毁。

这取决于人们想要什么,它可能是预期的或意外的行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多