Extending and Embedding Python, Release 3.9.5 Chapter 2. Creating extensions without third party tools 42 (continues on next page) (continued from previous page) if (vret!=0) return vret; } return 0; } static int Custom_traverse(CustomObject *self, visitproc visit, void *arg) { Py_VISIT(self->first); Py_VISIT(self->last); return 0; } static int Custom_clear(CustomObject *self) { Py_CLEAR(self->first); Py_CLEAR(self->last); return 0; } PyObject *tmp; tmp =self->first; self->first =NULL ; Py_XDECREF(tmp); static void Custom_dealloc(CustomObject *self) { The deallocator$_{Custom_dealloc}$ may call arbitrary code when clearing attributes. It means the circular GC can be triggered inside the function. Since the GC assumes reference count is not zero, we need to untrack the object from the GC by calling$_{PyObject_GC_UnTrack()}$ before clearing members. Here is our reimplemented deallocator using$_{PyObject_GC_UnTrack()}$ and$_{Custom_clear}$ : Nevertheless, it is much easier and less error-prone to always use$_{Py_CLEAR()}$ when deleting an attribute. Don’t try to micro-optimize at the expense of robustness! Note: You could emulate$_{Py_CLEAR()}$ by writing: Notice the use of the$_{Py_CLEAR()}$ macro. It is the recommended and safe way to clear data attributes of arbitrary types while decrementing their reference counts. If you were to call$_{Py_XDECREF()}$ instead on the attribute before setting it to$_{NULL}$ , there is a possibility that the attribute’s destructor would call back into code that reads the attribute again ( especially if there is a reference cycle). Second, we need to provide a method for clearing any subobjects that can participate in cycles: Note: The tp_traverse implementation must name its arguments exactly visit and arg in order to use Py_VISIT() . Python provides a Py_VISIT() macro that automates calling visit functions. With Py_VISIT() , we can mini- mize the amount of boilerplate in Custom_traverse : For each subobject that can participate in cycles, we need to call the visit() function, which is passed to the traversal method. The$_{visit()}$ function takes as arguments the subobject and the extra argument arg passed to the traversal method. It returns an integer value that must be returned if it is non-zero.