Several Python structures seem to need a sentinel (probably in order to know when to "stop"). But why do some, like arrays of PyMethodDef
, have a sentinel element initialized with multiple NULL
s?
For example zip
:
static PyMethodDef zip_methods[] = {
{"__reduce__", (PyCFunction)zip_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};
Why does the last PyMethodDef
in the "sentinel array" have the two NULL
s? Why not just 1? Or given that __reduce__
has 4 entries why not 4 NULL
s as sentinel element?
I don't think it does. For two reasons:
1) In the Python source code it only checks the name against NULL.
As far as I'm aware, PyMethodDef
arrays are used in two places: when attaching methods to a type, and when attaching methods to a module.
To find the relevant bit of code start by noting that all types go through PyType_Ready
and most modules go through PyModule_Init
so start the search there. PyModule_Create
forwards to PyModule_Create2
. In PyType_Ready
the methods get dealt with by the internal function add_methods
. In PyModule_Create2
there is all call to PyModule_AddFunctions
which is actually a public function if you want to do low level stuff yourself and which in turn calls the internal function _add_methods_to_object
.
Both of these internal functions have a for loop to loop over the methods and add them to the relevant dictionary. In both cases the condition to continue looping is meth->ml_name!=NULL
.
Therefore, at least currently, only the name is checked.
2) In both C and C++ partial initialization guarantees that the remaining fields are zero/default initialized. Therefore just initializing the first element of the sentinel to 0 ensures that all the other elements are initialized to 0. You can even just use {}
.
(As a side note, Python uses this automatic zero initialization a lot with the large structs it defines, for example PyTypeObject
which is huge and which you rarely bother filling in completely.)
After writing this answer I found that this had already been discussed.
So in summary - Python only checks the ml_name
(although that's an implementation detail so I guess could change in future if they find a use for a NULL
name with a non-NULL
method), and C automatically zeros the sentinel anyway. I don't know why the convention appears to be to set two elements, but there's something to be said from following convention.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With