Python 2.7的doc的两个部分提到为扩展模块中定义的容器对象添加循环垃圾收集(CGC)支持 .
Python/C API Reference Manual给出两条规则,即
必须使用PyObject_GC_New()或PyObject_GC_NewVar()分配对象的内存 . 一旦初始化了可能包含对其他容器的引用的所有字段,它就必须调用PyObject_GC_Track() .
而在Extending and Embedding the Python Interpreter中,对于 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支持的容器对象的简洁例子吗?
2 回答
我自己在C API方面没有足够的经验给你任何建议,但Python容器实现本身有很多例子 .
就个人而言,我'd start with the tuple implementation first, since it'不可变:Objects/tupleobject.c . 然后转到
dict
,list
和set
实现,以获取有关可变容器的更多说明:Objects/dictobject.c
Objects/listobject.c
Objects/setobject.c
我不禁注意到整个地方都有
PyObject_GC_New()
,PyObject_GC_NewVar()
和_570550的调用,并且设置了Py_TPFLAGS_HAVE_GC
.在大多数正常情况下,你不应该特别明确 . 在Noddy example的情况下,你绝对不会 .
简短版本是TypeObject包含两个函数指针:
tp_alloc
和tp_free
. 默认情况下,tp_alloc
在创建类时调用所有正确的函数(如果设置了Py_TPFLAGS_HAVE_GC
),并且tp_free
在破坏时调用类 .Noddy documentation says(在本节末尾):
不幸的是,一个不需要自己做的地方就是Supporting Cyclic Garbage Collection documentation .
Detail:
使用名为
Noddy_new
的函数在TypeObject
的tp_new
槽中分配Noddy . 根据the documentation,"new"函数应该做的主要事情是调用tp_alloc slot . 您通常不会自己编写tp_alloc
,它只是默认为PyType_GenericAlloc()
.查看PyType_GenericAlloc() in the Python source显示了基于
PyType_IS_GC(type)
更改的多个部分 . 首先它调用_PyObject_GC_Malloc
而不是PyObject_Malloc
,然后调用_PyObject_GC_TRACK(obj)
. [注意,PyObject_New
所做的全部是调用PyObject_Malloc
然后调用tp_init
. ]类似地,在重新分配时,您调用tp_free slot,对于具有
Py_TPFLAGS_HAVE_GC
的类,它会自动设置为PyObject_GC_Del .PyObject_GC_Del
包含与PyObject_GC_UnTrack
相同的代码,因此无需调用untrack .