0

I was reading through this tutorial in order to learn how to write C extension module to define new types. At some point the following tp_init implementation was given:

static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL, *tmp;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                     &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        tmp = self->first;
        Py_INCREF(first);
        self->first = first;
        Py_XDECREF(tmp);
    }
    if (last) {
        tmp = self->last;
        Py_INCREF(last);
        self->last = last;
        Py_XDECREF(tmp);
    }
    return 0;
}

The trouble I'm facing is that I can't see any harm to assign the first member like this:

if (first) {
    Py_XDECREF(self->first);
    Py_INCREF(first);
    self->first = first;
}

On the other hand, the said tutorial warn us from using it because:

Our type doesn’t restrict the type of the first member, so it could be any kind of object. It could have a destructor that causes code to be executed that tries to access the first member

It also goes on to state:

To be paranoid and protect ourselves against this possibility, we almost always reassign members before decrementing their reference counts

Taking those warnings into account, I still can't see any practical differences between the two ways. So, why should I use the first one which reassign the member first to a new PyObject before decrementing the old PyObject reference counts?

sifadil
  • 23
  • 1
  • 4
  • I did try to answer this but ended up deleting it because I was just repeating the documentation. Possibly the detail you're missing is that the `Py_XDECREF` can cause arbitrary Python code to be executed, and the C function won't continue until that Python code has finished executing? – DavidW May 27 '21 at 08:29
  • I believe the arbitrary Python code you're refering won't execute until the garbage collector kicks in and the PyObject's reference count reached zero. What I can't understand is how the recommended way allows us to avoid the issue compared to the non-recommended way? – sifadil May 27 '21 at 10:16
  • An objects refcount can reach 0 on any DECREF. The "garbage collector" is a separate feature that occasionally runs to find circular references. – DavidW May 27 '21 at 10:25

0 Answers0