Generic Attribute Management

New in version 2.2.

Most extension types only use simple attributes. So, what makes the attributes simple? There are only a couple of conditions that must be met:

  1. The name of the attributes must be known when PyType_Ready() is called.

  2. No special processing is needed to record that an attribute was looked up or set, nor do actions need to be taken based on the value.

Note that this list does not place any restrictions on the values of the attributes, when the values are computed, or how relevant data is stored.

When PyType_Ready() is called, it uses three tables referenced by the type object to create descriptors which are placed in the dictionary of the type object. Each descriptor controls access to one attribute of the instance object. Each of the tables is optional; if all three are NULL, instances of the type will only have attributes that are inherited from their base type, and should leave the tp_getattro and tp_setattro fields NULL as well, allowing the base type to handle attributes.

The tables are declared as three fields of the type object:

    struct PyMethodDef *tp_methods;
    struct PyMemberDef *tp_members;
    struct PyGetSetDef *tp_getset;

If tp_methods is not NULL, it must refer to an array of PyMethodDef structures. Each entry in the table is an instance of this structure:

typedef struct PyMethodDef {
    char        *ml_name;       /* method name */
    PyCFunction  ml_meth;       /* implementation function */
    int	         ml_flags;      /* flags */
    char        *ml_doc;        /* docstring */
} PyMethodDef;

One entry should be defined for each method provided by the type; no entries are needed for methods inherited from a base type. One additional entry is needed at the end; it is a sentinel that marks the end of the array. The ml_name field of the sentinel must be NULL.

XXX Need to refer to some unified discussion of the structure fields, shared with the next section.

The second table is used to define attributes which map directly to data stored in the instance. A variety of primitive C types are supported, and access may be read-only or read-write. The structures in the table are defined as:

typedef struct PyMemberDef {
    char *name;
    int   type;
    int   offset;
    int   flags;
    char *doc;
} PyMemberDef;

For each entry in the table, a descriptor will be constructed and added to the type which will be able to extract a value from the instance structure. The type field should contain one of the type codes defined in the structmember.h header; the value will be used to determine how to convert Python values to and from C values. The flags field is used to store flags which control how the attribute can be accessed.

XXX Need to move some of this to a shared section!

The following flag constants are defined in structmember.h; they may be combined using bitwise-OR.

Constant Meaning
READONLY Never writable.
RO Shorthand for READONLY.
READ_RESTRICTED Not readable in restricted mode.
WRITE_RESTRICTED Not writable in restricted mode.
RESTRICTED Not readable or writable in restricted mode.

An interesting advantage of using the tp_members table to build descriptors that are used at runtime is that any attribute defined this way can have an associated doc string simply by providing the text in the table. An application can use the introspection API to retrieve the descriptor from the class object, and get the doc string using its __doc__ attribute.

As with the tp_methods table, a sentinel entry with a name value of NULL is required.

See About this document... for information on suggesting changes.