liu*_*uyu 6 c python garbage-collection python-c-api python-extensions
Python 2.7的doc的两个部分提到为扩展模块中定义的容器对象添加循环垃圾收集(CGC)支持.
在Python的/ C API参考手册给出了两个规则,即
- 必须使用
PyObject_GC_New()或分配对象的内存PyObject_GC_NewVar().- 一旦初始化了可能包含对其他容器的引用的所有字段,它就必须调用
PyObject_GC_Track().
而在扩展和嵌入Python解释器中,Noddy例如,添加Py_TPFLAGS_HAVE_GC标志和填充tp_traverse以及tp_clear插槽似乎足以启用CGC支持.上面的两个规则根本没有实践.
当我修改Noddy示例以实际遵循PyObject_GC_New()/ PyObject_GC_Del()和PyObject_Track()/ 的规则时PyObject_GC_UnTrack(),令人惊讶地提出断言错误说,
Modules/gcmodule.c:348:visit_decref:断言"gc-> gc.gc_refs!= 0"失败.refcount太小了
这导致我对实现CGC的正确/安全方式感到困惑.任何人都可以给出建议,或者最好是一个有CGC支持的容器对象的简洁例子吗?
在大多数正常情况下,您不需要自己进行跟踪/取消跟踪。文档中对此进行了描述,但没有明确说明。在这个例子中Noddy你绝对不会。
简而言之,TypeObject 包含两个函数指针:tp_alloc和tp_free。默认情况下,tp_alloc在创建类时调用所有正确的函数(如果Py_TPFLAGS_HAVE_GC已设置),并tp_free在销毁时取消跟踪该类。
Noddy文档说(在本节的末尾):
\n\n\n\n\n\xe2\x80\x99 就差不多了。如果我们编写了自定义
\ntp_alloc或tp_free插槽,我们\xe2\x80\x99d需要修改它们以进行循环垃圾收集。大多数扩展将使用自动提供的版本。
不幸的是,有一个地方没有明确表示您不需要自己执行此操作,即支持循环垃圾收集文档。
\n\n细节:
\n\nNoddy 的分配是使用一个名为Noddy_newput 的tp_new函数放入TypeObject. 根据文档,“新”函数应该做的主要事情是调用tp_alloc槽。您通常不会tp_alloc自己编写,它只是默认为PyType_GenericAlloc().
查看PyType_GenericAlloc()Python 源代码会发现它基于PyType_IS_GC(type). 首先它调用_PyObject_GC_Malloc而不是PyObject_Malloc,然后它调用_PyObject_GC_TRACK(obj)。[请注意,真正PyObject_New要做的只是调用PyObject_Malloc然后tp_init。]
类似地,在释放时,您调用tp_freeslotPyObject_GC_Del ,对于带有 的类,它会自动设置为Py_TPFLAGS_HAVE_GC。PyObject_GC_Del包含与此相同的代码,PyObject_GC_UnTrack因此无需调用 untrack。