Type Objects

Python 对象系统最重要的结构之一可能是定义新类型的结构:PyTypeObject结构。可以使用PyObject_*()PyType_*()函数中的任何一种来处理类型对象,但是对于大多数 Python 应用程序而言,它们并没有提供很多有趣的东西。这些对象是对象行为方式的基础,因此它们对于解释器本身以及实现新类型的任何扩展模块都非常重要。

与大多数标准类型相比,类型对象相当大。其大小的原因是,每个类型对象都存储大量的值,主要是 C 函数指针,每个值都实现了类型Function的一小部分。本节将详细检查类型对象的字段。这些字段将按照它们在结构中出现的 Sequences 进行描述。

Typedefs:一元函数,二进制函数,三元函数,查询,强制,intargfunc,intintargfunc,intobjargproc,intintobjargproc,objobjargproc,析构函数,freefunc,printfunc,getattrfunc,getattrofunc,setattrfunc,setattrofunc,cmpf

PyTypeObject的结构定义可以在Include/object.h中找到。为了参考方便,此重复此处的定义:

typedef struct _typeobject {
    PyObject_VAR_HEAD
    char *tp_name; /* For printing, in format "<module>.<name>" */
    int tp_basicsize, tp_itemsize; /* For allocation */

    /* Methods to implement standard operations */

    destructor tp_dealloc;
    printfunc tp_print;
    getattrfunc tp_getattr;
    setattrfunc tp_setattr;
    cmpfunc tp_compare;
    reprfunc tp_repr;

    /* Method suites for standard classes */

    PyNumberMethods *tp_as_number;
    PySequenceMethods *tp_as_sequence;
    PyMappingMethods *tp_as_mapping;

    /* More standard operations (here for binary compatibility) */

    hashfunc tp_hash;
    ternaryfunc tp_call;
    reprfunc tp_str;
    getattrofunc tp_getattro;
    setattrofunc tp_setattro;

    /* Functions to access object as input/output buffer */
    PyBufferProcs *tp_as_buffer;

    /* Flags to define presence of optional/expanded features */
    long tp_flags;

    char *tp_doc; /* Documentation string */

    /* Assigned meaning in release 2.0 */
    /* call function for all accessible objects */
    traverseproc tp_traverse;

    /* delete references to contained objects */
    inquiry tp_clear;

    /* Assigned meaning in release 2.1 */
    /* rich comparisons */
    richcmpfunc tp_richcompare;

    /* weak reference enabler */
    long tp_weaklistoffset;

    /* Added in release 2.2 */
    /* Iterators */
    getiterfunc tp_iter;
    iternextfunc tp_iternext;

    /* Attribute descriptor and subclassing stuff */
    struct PyMethodDef *tp_methods;
    struct PyMemberDef *tp_members;
    struct PyGetSetDef *tp_getset;
    struct _typeobject *tp_base;
    PyObject *tp_dict;
    descrgetfunc tp_descr_get;
    descrsetfunc tp_descr_set;
    long tp_dictoffset;
    initproc tp_init;
    allocfunc tp_alloc;
    newfunc tp_new;
    freefunc tp_free; /* Low-level free-memory routine */
    inquiry tp_is_gc; /* For PyObject_IS_GC */
    PyObject *tp_bases;
    PyObject *tp_mro; /* method resolution order */
    PyObject *tp_cache;
    PyObject *tp_subclasses;
    PyObject *tp_weaklist;

} PyTypeObject;

类型对象结构扩展了PyVarObject结构。 ob_size字段用于动态类型(由type_new()创建,通常从类语句调用)。请注意,PyType_Type(元类型)会初始化tp_itemsize,这意味着其实例(即类型对象)必须具有ob_size字段。

这些字段不被子类型继承。

子类型不继承此字段。

在版本 2.5 中更改:此字段曾经是int类型。这可能需要更改您的代码以正确支持 64 位系统。

Foo_Type.ob_type = &PyType_Type;

这应该在创建任何类型的实例之前完成。 PyType_Ready()检查ob_type是否为* NULL *,如果是,则将其初始化:在 Python 2.2 中,将其设置为&PyType_Type;在 Python 2.2.1 和更高版本中,它被初始化为 Base Class 的ob_type字段。 PyType_Ready()如果该字段非零,则不会更改。

在 Python 2.2 中,此字段不被子类型继承。在 2.2.1 和 2.3 及更高版本中,它是由子类型继承的。

子类型不继承此字段。

对于动态分配的类型对象,它应该只是类型名称,而模块名称中明确存储的模块名称将作为键'__module__'的值。

对于静态分配的类型对象,tp_name 字段应包含一个点。最后一个点之前的所有内容都可以作为__module__属性访问,最后一个点之后的所有内容都可以作为name属性访问。

如果不存在点,则将整个tp_name字段作为name属性进行访问,并且未定义__module__属性(除非如上所述在字典中进行了明确设置)。这意味着您的类型将无法腌制。此外,它不会在 pydoc 创建的模块文档中列出。

子类型不继承此字段。

类型有两种:具有固定长度实例的类型具有零tp_itemsize字段,具有可变长度实例的类型具有非零tp_itemsize字段。对于具有固定长度实例的类型,所有实例的大小均相同,如tp_basicsize所示。

对于具有可变长度实例的类型,实例必须具有ob_size字段,并且实例大小为tp_basicsize加 N 乘以tp_itemsize,其中 N 是对象的“长度”。 N 的值通常存储在实例的ob_size字段中。有 exception:例如,长整数使用负数ob_size表示负数,而 N 在其中为abs(ob_size)。同样,实例布局中存在ob_size字段并不表示实例结构是可变长度的(例如,列表类型的结构具有固定长度的实例,但是这些实例具有有意义的ob_size字段)。

基本大小包括由宏PyObject_HEADPyObject_VAR_HEAD语句的实例中的字段(无论哪个用于语句实例结构),并且依次包含_ob_prev_ob_next字段(如果存在)。这意味着获取tp_basicsize的初始化程序的唯一正确方法是在用于语句实例布局的结构上使用sizeof运算符。基本大小不包括 GCHeaders 大小(这是 Python 2.2 中的新Function;在 2.1 和 2.0 中,tp_basicsize中包含了 GCHeaders 大小)。

这些字段由子类型分别继承。如果基本类型具有非零的tp_itemsize,则在子类型中将tp_itemsize设置为其他非零值通常是不安全的(尽管这取决于基本类型的实现)。

关于对齐的 Comments:如果变量项需要特定的对齐,则应passtp_basicsize的值来解决。示例:假设一个类型实现double的数组。 tp_itemsizesizeof(double)tp_basicsizesizeof(double)的倍数是程序员的责任(假设这是double的对齐要求)。

当新的引用计数为零时,Py_DECREF()Py_XDECREF()宏将调用析构函数。此时,该实例仍然存在,但是没有引用。析构函数应释放实例所拥有的所有引用,释放实例所拥有的所有内存缓冲区(使用与用于分配缓冲区的分配函数相对应的释放函数),最后(作为其最后一个动作)调用类型的tp_free函数。 。如果类型不是子类型(没有设置Py_TPFLAGS_BASETYPE标志位),则可以直接调用对象解除分配器,而不是passtp_free。对象取消分配器应该是用于分配实例的对象。如果使用PyObject_New()PyObject_VarNew()分配实例,则通常为PyObject_Del();如果使用PyObject_GC_New()PyObject_GC_NewVar()分配实例,则通常为PyObject_GC_Del()

该字段由子类型继承。

仅在将实例打印到* real 文件时才调用 print 函数;当将其打印到伪文件(例如StringIO实例)时,将调用实例的tp_reprtp_str函数将其转换为字符串。当类型的tp_print字段为 NULL *时,也将调用它们。类型绝不能以与tp_reprtp_str产生不同输出的方式实现tp_print

将使用与PyObject_Print()int tp_print(PyObject *self, FILE *file, int flags)相同的签名来调用打印Function。 * self *参数是要打印的实例。 * file *参数是要打印到的 stdio 文件。 * flags *参数由标志位组成。当前定义的唯一标志位是Py_PRINT_RAW。当Py_PRINT_RAW标志位置 1 时,实例的打印应与tp_str对其进行格式化的方式相同;当Py_PRINT_RAW标志位清零时,实例的打印应与tp_repr对其进行格式化的方式相同。当比较期间发生错误时,它应返回-1并设置异常条件。

tp_print字段可能会被弃用。无论如何,建议不要定义tp_print,而应依靠tp_reprtp_str进行打印。

该字段由子类型继承。

此字段已弃用。定义后,它应指向一个Function与tp_getattro函数相同的函数,但要使用 C 字符串而不是 Python 字符串对象来提供属性名称。签名是

PyObject * tp_getattr(PyObject *o, char *attr_name);

该字段由子类型与tp_getattro一起继承:当子类型的tp_getattrtp_getattro均为* NULL *时,子类型从其基本类型继承tp_getattrtp_getattro

此字段已弃用。定义后,它应指向一个Function与tp_setattro函数相同的函数,但要使用 C 字符串而不是 Python 字符串对象来提供属性名称。签名是

PyObject * tp_setattr(PyObject *o, char *attr_name, PyObject *v);

签名与PyObject_Compare()相同。如果* self 大于 other ,该函数应返回1,如果 self 等于 other 则返回0,如果 self 小于 other *则返回-1。当比较期间发生错误时,它应返回-1并设置异常条件。

该字段由子类型以及tp_richcomparetp_hash继承:当子类型的tp_comparetp_richcomparetp_hash均为* NULL *时,子类型继承tp_comparetp_richcomparetp_hash的全部三个。

签名与PyObject_Repr()相同;它必须返回字符串或 Unicode 对象。理想情况下,此函数应返回一个字符串,在给定合适的环境的情况下,该字符串在传递给eval()时将返回具有相同值的对象。如果这不可行,则应返回以'<'开头并以'>'结束的字符串,从该字符串中可以推断出对象的类型和值。

如果未设置此字段,则返回格式为<%s object at %p>的字符串,其中%s替换为类型名称,%p替换为对象的内存地址。

该字段由子类型继承。

tp_as_number字段不是继承的,但是包含的字段是分别继承的。

tp_as_sequence字段不是继承的,但是包含的字段是分别继承的。

tp_as_mapping字段不是继承的,但是包含的字段是分别继承的。

签名与PyObject_Hash()相同;它必须返回一个 C 长。值-1不应作为常规返回值返回;当在哈希值的计算过程中发生错误时,该函数应设置一个异常并返回-1

可以将该字段显式设置为PyObject_HashNotImplemented(),以阻止哈希方法从父类型继承。在 Python 级别上,这被解释为等效于__hash__ = None,从而导致isinstance(o, collections.Hashable)正确返回False。请注意,反之亦然-在 Python 级别的类上设置__hash__ = None将导致tp_hash插槽设置为PyObject_HashNotImplemented()

如果未设置此字段,则存在两种可能性:如果tp_comparetp_richcompare字段均为* NULL *,则返回基于对象地址的默认哈希值;否则,将返回默认值。否则,将引发TypeError

此字段由子类型以及tp_richcomparetp_compare继承:当子类型的tp_comparetp_richcomparetp_hash均为* NULL *时,子类型继承tp_comparetp_richcomparetp_hash的全部三个。

该字段由子类型继承。

签名与PyObject_Str()相同;它必须返回字符串或 Unicode 对象。此函数应返回对象的“友好”字符串表示形式,因为这是 print 语句将使用的表示形式。

如果未设置此字段,则调用PyObject_Repr()以返回字符串表示形式。

该字段由子类型继承。

签名与PyObject_GetAttr()相同。通常将此字段设置为PyObject_GenericGetAttr()很方便,这实现了查找对象属性的常规方法。

该字段由子类型与tp_getattr一起继承:当子类型的tp_getattrtp_getattro均为* NULL *时,子类型从其基本类型继承tp_getattrtp_getattro

签名与PyObject_SetAttr()相同,但是必须支持将* v 设置为 NULL *以删除属性。通常将此字段设置为PyObject_GenericSetAttr()很方便,这实现了设置对象属性的常规方法。

该字段由子类型与tp_setattr一起继承:当子类型的tp_setattrtp_setattro均为* NULL *时,子类型从其基本类型继承tp_setattrtp_setattro

tp_as_buffer字段不是继承的,但是包含的字段是分别继承的。

该字段的继承很复杂。大多数标志位是单独继承的,即,如果基本类型设置了标志位,则子类型将继承此标志位。如果继承了扩展结构,则严格继承与扩展结构有关的标志位,即标志位的基本类型值与指向扩展结构的指针一起复制到子类型中。 Py_TPFLAGS_HAVE_GC标志位与tp_traversetp_clear字段一起继承,即,如果子类型中的Py_TPFLAGS_HAVE_GC标志位是清除的,并且子类型中的tp_traversetp_clear字段存在(如Py_TPFLAGS_HAVE_RICHCOMPARE标志位所示)并且具有* NULL *值。

当前定义了以下位掩码;可以使用|运算符对它们进行“或”运算以形成tp_flags字段的值。宏PyType_HasFeature()接受类型和标志值* tp f *,并检查tp->tp_flags & f是否为非零。

该字段不是子类型继承的。

仅当设置了Py_TPFLAGS_HAVE_RICHCOMPARE标志位时,才存在以下三个字段。

垃圾回收器使用tp_traverse指针来检测参考周期。 tp_traverse函数的典型实现只是在实例的每个 Python 对象成员上调用Py_VISIT()。例如,这是thread扩展模块中的函数local_traverse()

static int
local_traverse(localobject *self, visitproc visit, void *arg)
{
    Py_VISIT(self->args);
    Py_VISIT(self->kw);
    Py_VISIT(self->dict);
    return 0;
}

请注意,Py_VISIT()仅在可以参与参考循环的那些成员上被调用。尽管也有self->key成员,但它只能是* NULL *或 Python 字符串,因此不能成为参考循环的一部分。

另一方面,即使您知道某个成员永远都不可能成为循环的一部分,作为调试辅助工具,您可能仍想访问它,以便gc模块的get_referents()函数将其包括在内。

请注意,Py_VISIT()要求local_traverse()的* visit arg *参数具有这些特定名称;不要给他们起任何名字。

该字段由子类型与tp_clearPy_TPFLAGS_HAVE_GC标志位一起继承:如果在子类型*中并且它们都设置了Py_TPFLAGS_HAVE_RICHCOMPARE标志位,则标志位tp_traversetp_clear都从基本类型继承而来。

tp_clear成员函数用于break垃圾收集器检测到的循环垃圾中的参考周期。总而言之,系统中的所有tp_clear函数必须结合起来才能break所有参考周期。这是微妙的,如果有任何疑问,请提供tp_clear函数。例如,Tuples 类型不实现tp_clear函数,因为有可能证明没有引用周期可以完全由 Tuples 组成。因此,其他类型的tp_clear函数必须足以break包含 Tuples 的任何循环。这不是立即显而易见的,很少有充分的理由避免实现tp_clear

tp_clear的实现应删除实例对其可能是 Python 对象的成员的引用,并将其指向那些成员的指针设置为* NULL *,如以下示例所示:

static int
local_clear(localobject *self)
{
    Py_CLEAR(self->key);
    Py_CLEAR(self->args);
    Py_CLEAR(self->kw);
    Py_CLEAR(self->dict);
    return 0;
}

应该使用Py_CLEAR()宏,因为清除引用非常困难:对包含对象的引用必须在指向包含对象的指针设置为* NULL 之后才能递减。这是因为减少引用计数可能会导致所包含的对象变成垃圾,从而触发一连串的回收活动,该活动可能包括调用任意 Python 代码(由于与所包含的对象相关联的终结器或 weakref 回调)。如果这样的代码有可能再次引用 self ,那么到那时包含对象的指针为 NULL 很重要,这样 self *知道不再可以使用包含的对象。 Py_CLEAR()宏以安全的 Sequences 执行操作。

由于tp_clear函数的目标是 break 参考周期,因此不必清除无法参与参考周期的包含对象,如 Python 字符串或 Python 整数。另一方面,清除所有包含的 Python 对象并编写类型的tp_dealloc函数来调用tp_clear可能会很方便。

有关 Python 的垃圾回收方案的更多信息,请参见支持循环垃圾收集部分。

该字段由子类型与tp_traversePy_TPFLAGS_HAVE_GC标志位一起继承:如果在子类型*中并且它们都设置了Py_TPFLAGS_HAVE_RICHCOMPARE标志位,则标志位tp_traversetp_clear都从基本类型继承而来。

该函数应返回比较结果(通常为Py_TruePy_False)。如果比较未定义,则必须返回Py_NotImplemented,如果发生另一个错误,则必须返回NULL并设置异常条件。

Note

如果您想实现仅对有限的一组比较有意义的类型(例如==!=,而不是<和朋友),请直接在丰富的比较Function中提高TypeError

该字段由子类型以及tp_comparetp_hash继承:当子类型的tp_comparetp_richcomparetp_hash均为* NULL *时,子类型继承tp_comparetp_richcomparetp_hash的全部三个。

以下常量被定义为tp_richcomparePyObject_RichCompare()的第三个参数:

Constant Comparison
Py_LT <
Py_LE <=
Py_EQ ==
Py_NE !=
Py_GT >
Py_GE >=

仅当设置了Py_TPFLAGS_HAVE_WEAKREFS标志位时,下一个字段才存在。

不要将此字段与tp_weaklist混淆;这是对类型对象本身的弱引用的列表头。

该字段由子类型继承,但请参见下面列出的规则。子类型可以覆盖此偏移量。这意味着子类型使用与基本类型不同的弱引用列表头。由于列表头始终是passtp_weaklistoffset找到的,因此这不是问题。

当由类语句定义的类型没有__slots__语句,并且其基本类型均不可弱引用时,可以pass在实例布局中添加弱引用列表头插槽并设置该插槽的偏移量tp_weaklistoffset来使该类型成为弱引用。

当类型的slots语句包含名为__weakref__的插槽时,该插槽将成为该类型实例的弱引用列表头,并且该插槽的偏移量存储在该类型的tp_weaklistoffset中。

当类型的slots语句不包含名为__weakref__的插槽时,该类型将从其基本类型继承其tp_weaklistoffset

仅当设置了Py_TPFLAGS_HAVE_ITER标志位时,接下来的两个字段才存在。

此Function具有与PyObject_GetIter()相同的签名。

该字段由子类型继承。

迭代器类型还应该定义tp_iter函数,并且该函数应返回迭代器实例本身(而不是新的迭代器实例)。

此Function具有与PyIter_Next()相同的签名。

该字段由子类型继承。

仅当设置了Py_TPFLAGS_HAVE_CLASS标志位时,下一个字段(包括tp_weaklist在内)才存在。

对于数组中的每个条目,将一个条目添加到包含方法 Descriptors 的类型的字典中(请参见下面的tp_dict)。

子类型不会继承该字段(方法是pass其他机制继承的)。

对于数组中的每个条目,将一个条目添加到包含成员 Descriptors 的类型的字典中(请参见下面的tp_dict)。

子类型不继承该字段(成员pass不同的机制继承)。

对于数组中的每个条目,将一个条目添加到包含 getsetDescriptors 的类型的字典中(请参见下面的tp_dict)。

子类型不会继承此字段(计算的属性是pass其他机制继承的)。

显然,该字段不被子类型继承,但默认为&PyBaseObject_Type(对于 Python 程序员来说,该类型称为object)。

在调用 PyType_Ready 之前,通常应将该字段初始化为* NULL *;它也可以初始化为包含该类型的初始属性的字典。 PyType_Ready()初始化类型后,只有该类型的额外属性不对应于重载的操作(例如add()),才可以将其添加到此字典中。

子类型不会继承该字段(尽管此处定义的属性是pass其他机制继承的)。

Function签名是

PyObject * tp_descr_get(PyObject *self, PyObject *obj, PyObject *type);

该字段由子类型继承。

Function签名是

int tp_descr_set(PyObject *self, PyObject *obj, PyObject *value);

不要将此字段与tp_dict混淆;那是类型对象本身的属性的字典。

如果此字段的值大于零,则它指定从实例结构开始的偏移量。如果该值小于零,则指定与实例结构的* end *的偏移量。负偏移量使用起来更昂贵,并且仅当实例结构包含可变长度部分时才应使用。例如,这用于将实例变量字典添加到strtuple的子类型。注意,在这种情况下,即使基本对象布局中不包含字典,tp_basicsize字段也应考虑添加到末尾的字典。在指针大小为 4 个字节的系统上,应将tp_dictoffset设置为-4以指示字典位于结构的最末端。

实例中的实际字典偏移量可以根据负数tp_dictoffset进行如下计算:

dictoffset = tp_basicsize + abs(ob_size)*tp_itemsize + tp_dictoffset
if dictoffset is not aligned on sizeof(void*):
    round up to sizeof(void*)

其中tp_basicsizetp_itemsizetp_dictoffset是从类型对象中获取的,而ob_size是从实例中获取的。之所以采用绝对值,是因为长整数使用ob_size的符号来存储数字的符号。 (永远不需要自己进行此计算;它由_PyObject_GetDictPtr()为您完成.)

该字段由子类型继承,但请参见下面列出的规则。子类型可以覆盖此偏移量。这意味着子类型实例以与基本类型不同的偏移量存储字典。由于字典始终是passtp_dictoffset找到的,因此这不是问题。

当由类语句定义的类型没有__slots__语句,并且其基本类型都没有实例变量字典时,会将字典槽添加到实例布局,并将tp_dictoffset设置为该槽的偏移量。

当由类语句定义的类型具有slots语句时,该类型将从其基本类型继承其tp_dictoffset

(在slots语句中添加名为dict的插槽并没有达到预期的效果,只会引起混淆。也许应该像__weakref__一样将此Function添加为Function。)

此函数对应于init()类的方法。像init()一样,可以在不调用init()的情况下创建实例,并且可以pass再次调用其init()方法来重新初始化实例。

Function签名是

int tp_init(PyObject *self, PyObject *args, PyObject *kwds)

self 参数是要初始化的实例; * args kwds *参数表示对init()的调用的位置和关键字参数。

当类型的tp_new函数返回该类型的实例后,pass调用其类型来正常创建实例时,将调用tp_init函数(如果不是* NULL )。如果tp_new函数返回的不是原始类型的子类型的某个其他类型的实例,则不会调用tp_init函数;否则,将调用tp_init函数。如果tp_new返回原始类型的子类型的实例,则调用该子类型的tp_init。 (版本 Comments:此处描述的是在 Python 2.2.1 及更高版本中实现的Function.在 Python 2.2 中,始终调用tp_new返回的对象类型的tp_init,如果不是 NULL *的话。)

该字段由子类型继承。

Function签名是

PyObject *tp_alloc(PyTypeObject *self, Py_ssize_t nitems)

此Function的目的是将内存分配与内存初始化分开。它应返回一个指向实例的足够长的内存块的指针,该内存块适当对齐,并初始化为零,但将ob_refcnt设置为1并将ob_type设置为 type 参数。如果类型的tp_itemsize非零,则应将对象的ob_size字段初始化为* nitems ,并且分配的内存块的长度应为tp_basicsize + nitems*tp_itemsize,四舍五入为sizeof(void*)的倍数;否则,不使用 nitems *,并且块的长度应为tp_basicsize

不要使用此函数进行任何其他实例初始化,甚至不要分配额外的内存。应该由tp_new完成。

该字段是由静态子类型继承的,而不是由动态子类型(由类语句创建的子类型)继承的;在后者中,此字段始终设置为PyType_GenericAlloc(),以强制执行标准堆分配策略。这也是静态定义类型的建议值。

如果该函数对于特定类型为* NULL *,则无法调用该类型以创建新实例;大概还有其他创建实例的方法,例如工厂函数。

Function签名是

PyObject *tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)

子类型参数是要创建的对象的类型。 * args kwds *参数表示对该类型的调用的位置和关键字参数。注意,子类型不必等于调用tp_new函数的类型。它可能是该类型的子类型(但不是无关类型)。

tp_new函数应调用subtype->tp_alloc(subtype, nitems)为该对象分配空间,然后仅在绝对必要的情况下进行更多的初始化。可以安全地忽略或重复进行的初始化应放在tp_init处理程序中。一个好的经验法则是,对于不可变类型,所有初始化都应在tp_new中进行,而对于可变类型,大多数初始化应推迟到tp_init进行。

该字段由子类型继承,除了它不是由tp_base为* NULL *或&PyBaseObject_Type的静态类型继承。后一个 exception 是一种预防措施,以防止旧的扩展类型仅pass与 Python 2.2 链接就不能被调用。

该函数的签名已稍有变化:在 Python 2.2 和 2.2.1 中,其签名为destructor

void tp_free(PyObject *)

在 Python 2.3 及更高版本中,其签名为freefunc

void tp_free(void *)

与这两个版本兼容的唯一初始化器是_PyObject_Del,其定义已在 Python 2.3 中进行了适当调整。

该字段是由静态子类型继承的,而不是由动态子类型(由类语句创建的子类型)继承的;在后者中,此字段设置为适合与PyType_GenericAlloc()Py_TPFLAGS_HAVE_GC标志位的值匹配的解除分配器。

垃圾收集器需要知道特定对象是否可收集。通常,查看对象类型的tp_flags字段并检查Py_TPFLAGS_HAVE_GC标志位就足够了。但是某些类型混合了静态和动态分配的实例,并且静态分配的实例是不可收集的。这些类型应定义此Function。对于可收集的实例,它应返回1,对于不可收集的实例应返回0。签名是

int tp_is_gc(PyObject *self)

(这种情况的唯一示例就是类型本身.元类型PyType_Type定义了此函数,以区分静态分配类型和动态分配类型。)

该字段由子类型继承。 (版本 Comments:在 Python 2.2 中,它没有被继承.在 2.2.1 和更高版本中被继承.)

这是为由类语句创建的类型设置的。对于静态定义的类型,它应该为* NULL *。

此字段未继承。

此字段未继承;它由PyType_Ready()重新计算。

其余字段仅在定义了Function测试宏COUNT_ALLOCS的情况下定义,并且仅供内部使用。为了完整起见,此处记录了它们。这些字段都不是子类型继承的。请参阅 PYTHONSHOWALLOCCOUNT环境变量。

此外,请注意,在垃圾回收的 Python 中,可以从任何 Python 线程调用 tp_dealloc,而不仅仅是创建对象的线程(如果该对象成为 refcount 周期的一部分,则该周期可以由任何对象上的垃圾回收来收集线)。对于 Python API 调用而言,这不是问题,因为在其上调用 tp_dealloc 的线程将拥有全局解释器锁(GIL)。但是,如果要销毁的对象又销毁了其他 C 或 C 库中的对象,则应注意确保销毁线程上名为 tp_dealloc 的那些对象不会违反该库的任何假设。

数字对象结构

这是结构定义:

typedef struct {
     binaryfunc nb_add;
     binaryfunc nb_subtract;
     binaryfunc nb_multiply;
     binaryfunc nb_divide;
     binaryfunc nb_remainder;
     binaryfunc nb_divmod;
     ternaryfunc nb_power;
     unaryfunc nb_negative;
     unaryfunc nb_positive;
     unaryfunc nb_absolute;
     inquiry nb_nonzero;       /* Used by PyObject_IsTrue */
     unaryfunc nb_invert;
     binaryfunc nb_lshift;
     binaryfunc nb_rshift;
     binaryfunc nb_and;
     binaryfunc nb_xor;
     binaryfunc nb_or;
     coercion nb_coerce;       /* Used by the coerce() function */
     unaryfunc nb_int;
     unaryfunc nb_long;
     unaryfunc nb_float;
     unaryfunc nb_oct;
     unaryfunc nb_hex;

     /* Added in release 2.0 */
     binaryfunc nb_inplace_add;
     binaryfunc nb_inplace_subtract;
     binaryfunc nb_inplace_multiply;
     binaryfunc nb_inplace_divide;
     binaryfunc nb_inplace_remainder;
     ternaryfunc nb_inplace_power;
     binaryfunc nb_inplace_lshift;
     binaryfunc nb_inplace_rshift;
     binaryfunc nb_inplace_and;
     binaryfunc nb_inplace_xor;
     binaryfunc nb_inplace_or;

     /* Added in release 2.2 */
     binaryfunc nb_floor_divide;
     binaryfunc nb_true_divide;
     binaryfunc nb_inplace_floor_divide;
     binaryfunc nb_inplace_true_divide;

     /* Added in release 2.5 */
     unaryfunc nb_index;
} PyNumberMethods;

根据标志位Py_TPFLAGS_CHECKTYPES,二进制和三进制函数可能会接收不同种类的参数:

如果未为给定的操作数定义操作,则二进制和三进制函数必须返回Py_NotImplemented,如果发生另一个错误,则它们必须返回NULL并设置异常。

Map 对象结构

序列对象结构

负索引的处理方式如下:如果填充了sq_length插槽,则会调用它,并且使用序列长度来计算传递给sq_item的正索引。如果sq_length为* NULL *,则索引按原样传递给函数。

缓冲区对象结构

缓冲区接口导出一个模型,在该模型中,对象可以将其内部数据公开为一组数据块,其中每个块均指定为指针/长度对。这些块称为* segments *,并假定它们在内存中不连续。

如果对象不导出缓冲区接口,则其在PyTypeObject结构中的tp_as_buffer成员应为* NULL *。否则,tp_as_buffer将指向PyBufferProcs结构。

Note

PyTypeObject结构使用Py_TPFLAGS_DEFAULT作为tp_flags成员的值而不是0非常重要。这告诉 Python 运行时您的PyBufferProcs结构包含bf_getcharbuffer插槽。较旧的 Python 版本没有此成员,因此使用旧 extensions 的新 Python 解释器需要能够在使用前测试其存在性。

第一个插槽是bf_getreadbuffer,类型为readbufferproc。如果此插槽为* NULL ,则该对象不支持从内部数据读取。这是没有意义的,因此实现者应填写此信息,但调用者应测试该插槽包含非 NULL *值。

下一个广告位是bf_getwritebuffer,其类型为writebufferproc。如果对象不允许写入其返回的缓冲区,则此插槽可能为* NULL *。

第三个插槽是bf_getsegcount,类型为segcountproc。该插槽不能为* NULL *,用于通知调用方对象包含多少段。诸如PyString_TypePyBuffer_Type对象之类的简单对象包含一个段。

最后一个插槽是bf_getcharbuffer,类型为charbufferproc。仅当对象PyTypeObjecttp_flags字段中存在Py_TPFLAGS_HAVE_GETCHARBUFFER标志时,才会显示此插槽。在使用此插槽之前,调用者应使用PyType_HasFeature()函数测试它是否存在。如果存在该标志,则bf_getcharbuffer可以为* NULL ,指示该对象的内容不能用作 8 位字符*。如果对象的内容不能解释为 8 位字符,则 slot 函数也可能会引发错误。例如,如果对象是配置为保存浮点值的数组,则在调用方try使用bf_getcharbuffer来获取 8 位字符序列的情况下,可能会引发异常。将内部缓冲区导出为“文本”的概念用于区分本质上为二进制的对象和具有基于字符的内容的对象。

Note

当前的 Policy 似乎规定这些字符可能是多字节字符。这意味着* N 的缓冲区大小并不意味着存在 N *个字符。

首页