扩展模块被垃圾回收时运行cython代码

时间:2019-01-28 18:32:18

标签: cython cpython python-internals

是否可以在Cython扩展模块中注册函数,以便在Python垃圾回收器破坏模块对象时调用此函数?

类似

def __dealloc__(...)

在模块级别?

1 个答案:

答案 0 :(得分:2)

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

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

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

_o=Observer()

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

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

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

问题:无论如何都无法重新加载cython扩展名(例如,通过importlib.reload()),因此只有在Python解释器关闭时,它才被释放。但是,然后就不需要取消分配资源了……

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

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

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时,如果调用了module deleted,我们也会看到importlib.reload(foo)。在这种情况下,不使用“ module_dealloc”,但是将清除对全局_o的所有引用,并销毁对象。

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