__del__ is not called after creating a new reference

Oleg Nesterov oleg at redhat.com
Fri Mar 17 10:54:59 EDT 2017


I started to learn python a few days ago and I am trying to understand what
__del__() actually does. https://docs.python.org/3/reference/datamodel.html
says:

	object.__del__(self)
	...
	Note that it is possible (though not recommended!) for the __del__()
	method to postpone destruction of the instance by creating a new
	reference to it. It may then be called at a later time when this new
	reference is deleted.

However, this trivial test-case

	class C:
		def __del__(self):
			print("DEL")
			global X
			X = self
	C()
	print(X)
	X = 0
	print(X)

shows that __del__ is called only once, it is not called again after "X = 0":

	DEL
	<__main__.C object at 0x7f067695f4a8>
	0
	
(Just in case, I verified later that this object actually goes away and its
 memory is freed, so the problem is not that it still has a reference).

I've cloned https://github.com/python/cpython.git and everything looks clear
at first glance (but let me repeat that I am very new to python):

	PyObject_CallFinalizerFromDealloc() calls PyObject_CallFinalizer()
	which finally calls "__del__" method in slot_tp_finalize(), then it
	notices that "X = self" creates the new reference and does:

	/* tp_finalize resurrected it!  Make it look like the original Py_DECREF
	 * never happened.
	 */
	refcnt = self->ob_refcnt;
	_Py_NewReference(self);
	self->ob_refcnt = refcnt;

However, PyObject_CallFinalizer() also does _PyGC_SET_FINALIZED(self, 1)
and that is why __del__ is not called again after "X = 0":

	/* tp_finalize should only be called once. */
	if (PyType_IS_GC(tp) && _PyGC_FINALIZED(self))
		return;

The comment and the code are very explicit, so this does nt look like a
bug in cpython.

Probably the docs should be fixed?

Or this code is actually wrong? The test-case works as documented if I
remove _PyGC_SET_FINALIZED() in PyObject_CallFinalizer() or add another
_PyGC_SET_FINALIZED(self, 0) into PyObject_CallFinalizerFromDealloc()
after _Py_NewReference(self), but yes, yes, I understand that this is
not correct and won't really help.

Oleg.



More information about the Python-list mailing list