[Python-3000] A better way to initialize PyTypeObject

Talin talin at acm.org
Tue Nov 28 10:22:07 CET 2006


More on the 'cruft removal' topic:

I notice that a number of fields in PyTypeObject are deprecated; the 
question is, how to get rid of them without gratuitously breaking 
everything? Even if there's zero code out there that still uses 
tp_getattr instead of tp_getattro, you can't simply remove the field 
because it takes up a placeholder slot in the C initializer list.

Has anyone proposed the notion of getting away from C-style initializer 
lists, at least for the case of PyTypeObject?

For example, consider the case for tp_init and tp_new. Currently these 
are filled in with the C initializer list for PyTypeObject. Because 
these are the 38th and 40th fields in PyTypeObject, you have to put 37 
initializer values before them, whether you use them or not.

Instead of doing that, however, you could put them in the method table, 
tp_methods:

    static PyMethodDef Noddy_methods[] = {
        { SPECMETH_NEW, (PyCFunction)Noddy_new, METH_VARARGS, "..." },
        { SPECMETH_INIT, (PyCFunction)Noddy_init, METH_VARARGS, "..." },
        { NULL }  /* Sentinel */
    };

'SPECMETH_NEW' and 'SPECMETH_INIT' are sentinel values indicating that 
these are 'special' methods, which are ignored during the normal 
scanning of this table. Instead, when the type is initialized, the 
method table is scanned for these special methods, and the corresponding 
field in PyTypeObject is filled in with the method pointer. The same 
holds true for all of the other special method pointers.

Now, you still have the problem that 'tp_methods' itself is something 
like the 28th field in the struct, which means you still have all of 
those empty fields to consider. So instead, make a new version of 
PyType_Ready which takes an optional parameter of the method table for 
the type, which is filled in as part of the initialization.

You could add another parameter for filling of data fields, which would 
point to a similar table that would hold values for non-function 
initializers.

What you end up with is code that looks like this:

PyTypeObject myType = {
     PyObject_HEAD_INIT(NULL)
     0,
     "myType",
     sizeof(myInstance)
}

void init() {
     if (PyType_ReadyInit( &myType, myTypeMethods, myTypeData ) < 0)
	return;
}

Note that this requires no change to the PyTypeObject struct itself, or 
of any code that currently uses it - the only thing that changes is the 
way that the struct's data fields are filled in.

What this would give you is a way to construct types that would be 
immune to future revisions of the PyTypeObject struct; You could add, 
remove, rename, or re-order the fields in the struct without breaking 
existing code.

(If there's an existing proposal for this, feel free to post a 
reference. I did look :)

-- Talin


More information about the Python-3000 mailing list