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字段。

  • PyObject* PyObject._ob_next
  • PyObject* PyObject._ob_prev
    • 这些字段仅在定义宏Py_TRACE_REFS时出现。 PyObject_HEAD_INIT宏负责将它们初始化为* NULL 。对于静态分配的对象,这些字段始终为 NULL *。对于动态分配的对象,这两个字段用于将对象链接到堆上所有所有活动对象的双向链接列表中。这可以用于各种调试目的。当前,唯一的用途是在设置了环境变量 PYTHONDUMPREFS时,在运行结束时打印仍然存在的对象。

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

  • Py_ssize_t PyObject.ob_refcnt
    • 这是类型对象的引用计数,由PyObject_HEAD_INIT宏初始化为1。请注意,对于静态分配的类型对象,类型的实例(ob_type指向类型的对象)作为引用。但是对于动态分配的类型对象,实例* do *被视为引用。

子类型不继承此字段。

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

  • PyTypeObject* PyObject.ob_type
    • 这是类型的类型,换句话说就是它的元类型。它由PyObject_HEAD_INIT宏的参数初始化,其值通常应为&PyType_Type。但是,对于必须在 Windows 上使用(至少)的动态可加载扩展模块,编译器会抱怨这不是有效的初始化程序。因此,惯例是将* NULL *传递给PyObject_HEAD_INIT宏,并在执行其他任何操作之前在模块的初始化函数开始时显式初始化此字段。通常这样做是这样的:
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 及更高版本中,它是由子类型继承的。

  • Py_ssize_t PyVarObject.ob_size
    • 对于静态分配的类型对象,应将其初始化为零。对于动态分配的类型对象,此字段具有特殊的内部含义。

子类型不继承此字段。

  • 字符* PyTypeObject.tp_name
    • 指向包含类型名称的 NUL 终止的字符串的指针。对于可以作为模块全局变量访问的类型,字符串应为完整的模块名称,后跟一个圆点,然后是类型名称;对于内置类型,它应该只是类型名称。如果模块是软件包的子模块,则完整的软件包名称是完整模块名称的一部分。例如,在包P的子包Q的模块M中定义的名为T的类型应具有tp_name初始值设定项"P.Q.M.T"

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

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

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

子类型不继承此字段。

  • Py_ssize_t PyTypeObject.tp_basicsize
  • Py_ssize_t PyTypeObject.tp_itemsize
    • 这些字段允许计算类型实例的字节大小。

类型有两种:具有固定长度实例的类型具有零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的对齐要求)。

  • 析构函数PyTypeObject.tp_dealloc
    • 指向实例析构函数的指针。除非该类型保证其实例永远不会被释放(如单例NoneEllipsis),否则必须定义此函数。

当新的引用计数为零时,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()

该字段由子类型继承。

  • printfunc PyTypeObject.tp_print
    • 指向实例打印Function的可选指针。

仅在将实例打印到* 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进行打印。

该字段由子类型继承。

  • getattrfunc PyTypeObject.tp_getattr
    • 指向 get-attribute-string 函数的可选指针。

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

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

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

  • setattrfunc PyTypeObject.tp_setattr
    • 指向用于设置和删除属性的函数的可选指针。

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

PyObject * tp_setattr(PyObject *o, char *attr_name, PyObject *v);
  • cmpfunc PyTypeObject.tp_compare
    • 指向三向比较Function的可选指针。

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

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

  • 代表PyTypeObject.tp_repr
    • 指向实现内置函数repr()的函数的可选指针。

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

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

该字段由子类型继承。

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

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

  • PyMappingMethods* tp_as_mapping
    • 指向包含仅与实现 Map 协议的对象相关的字段的附加结构的指针。这些字段记录在Map 对象结构中。

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

  • hashfunc PyTypeObject.tp_hash
    • 指向实现内置函数hash()的函数的可选指针。

签名与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的全部三个。

  • 三元函数PyTypeObject.tp_call
    • 指向实现调用对象的函数的可选指针。如果对象不可调用,则该值为* NULL *。签名与PyObject_Call()相同。

该字段由子类型继承。

  • 代表PyTypeObject.tp_str
    • 指向实现内置操作str()的函数的可选指针。 (请注意,str现在是一种类型,str()调用该类型的构造函数。此构造函数调用PyObject_Str()进行实际工作,而PyObject_Str()将调用此处理程序。)

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

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

该字段由子类型继承。

  • getattrofunc PyTypeObject.tp_getattro
    • 指向 get-attribute 函数的可选指针。

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

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

  • setattrofunc PyTypeObject.tp_setattro
    • 指向用于设置和删除属性的函数的可选指针。

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

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

  • PyBufferProcs* PyTypeObject.tp_as_buffer
    • 指向包含仅与实现缓冲区接口的对象相关的字段的附加结构的指针。这些字段记录在缓冲区对象结构中。

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

  • PyTypeObject.tp_flags
    • 该字段是各种标志的位掩码。一些标志指示在某些情况下的变体语义。其他用于指示类型对象(或在passtp_as_numbertp_as_sequencetp_as_mappingtp_as_buffer引用的扩展结构中)的某些字段在历史上并不总是有效的;如果清除了此类标志位,则必须禁止访问其保护的类型字段,而必须将其视为零或* NULL *值。

该字段的继承很复杂。大多数标志位是单独继承的,即,如果基本类型设置了标志位,则子类型将继承此标志位。如果继承了扩展结构,则严格继承与扩展结构有关的标志位,即标志位的基本类型值与指向扩展结构的指针一起复制到子类型中。 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_GETCHARBUFFER

  • Py_TPFLAGS_HAVE_SEQUENCE_IN

    • 如果设置此位,则tp_as_sequence引用的PySequenceMethods结构具有sq_contains字段。
  • Py_TPFLAGS_GC

    • 该位已过时。它用来命名的位不再使用。现在将符号定义为零。
  • Py_TPFLAGS_HAVE_INPLACEOPS

    • 如果设置此位,则tp_as_sequence引用的PySequenceMethods结构和tp_as_number引用的PyNumberMethods结构包含就地运算符的字段。特别地,这意味着PyNumberMethods结构具有字段nb_inplace_addnb_inplace_subtractnb_inplace_multiplynb_inplace_dividenb_inplace_remaindernb_inplace_powernb_inplace_lshiftnb_inplace_rshiftnb_inplace_andnb_inplace_xornb_inplace_orPySequenceMethods结构具有字段sq_inplace_concatsq_inplace_repeat
  • Py_TPFLAGS_CHECKTYPES

    • 如果设置了此位,则由tp_as_number引用的PyNumberMethods结构中的二进制和三元运算接受任意对象类型的参数,并在需要时进行自己的类型转换。如果清除此位,则这些操作要求所有参数均具有当前类型作为其类型,并且调用方应首先执行强制操作。这适用于nb_addnb_subtractnb_multiplynb_dividenb_remaindernb_divmodnb_powernb_lshiftnb_rshiftnb_andnb_xornb_or
  • Py_TPFLAGS_HAVE_RICHCOMPARE

  • Py_TPFLAGS_HAVE_WEAKREFS

  • Py_TPFLAGS_HAVE_ITER

  • Py_TPFLAGS_HAVE_CLASS

  • Py_TPFLAGS_HEAPTYPE

    • 当类型对象本身在堆上分配时,将设置此位。在这种情况下,其实例的ob_type字段被认为是对类型的引用,并且在创建新实例时将类型对象 INCREF'ed,在销毁实例时将类型 DECREF'd(不适用于以下类型的实例)子类型;仅实例的 ob_type 引用的类型被 INCREF(或 DECREF)。
  • Py_TPFLAGS_BASETYPE

    • 当该类型可用作另一种类型的基本类型时,将设置此位。如果清除此位,则该类型不能被子类型化(类似于 Java 中的“finally”类)。
  • Py_TPFLAGS_READY

    • 当类型对象已由PyType_Ready()完全初始化时,将设置此位。
  • Py_TPFLAGS_READYING

    • PyType_Ready()正在初始化类型对象的过程中,该位置 1.
  • Py_TPFLAGS_HAVE_GC

  • Py_TPFLAGS_DEFAULT

  • 字符* PyTypeObject.tp_doc

    • 指向 NUL 终止的 C 字符串的可选指针,为该类型对象提供 docstring。这在类型和类型的实例上显示为__doc__属性。

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

仅当设置了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都从基本类型继承而来。

  • inquiry PyTypeObject.tp_clear
    • 指向垃圾回收器的清除函数的可选指针。仅在设置了Py_TPFLAGS_HAVE_GC标志位时使用。

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都从基本类型继承而来。

  • richcmpfunc PyTypeObject.tp_richcompare
    • 指向丰富比较Function的可选指针,其签名为PyObject *tp_richcompare(PyObject *a, PyObject *b, int op)

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

Note

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

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

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

ConstantComparison
Py_LT<
Py_LE<=
Py_EQ==
Py_NE!=
Py_GT>
Py_GE>=

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

  • PyTypeObject.tp_weaklistoffset
    • 如果此类型的实例是弱引用的,则此字段大于零,并且包含弱引用列表头的实例结构中的偏移量(忽略 GC 头,如果存在);该偏移量由PyObject_ClearWeakRefs()PyWeakref_*()函数使用。实例结构需要包含类型为PyObject*的字段,该字段初始化为* NULL *。

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

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

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

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

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

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

  • getiterfunc PyTypeObject.tp_iter
    • 指向函数的可选指针,该函数返回对象的迭代器。它的存在通常表示该类型的实例是可迭代的(尽管不使用此Function,序列也可能是可迭代的,并且经典实例始终具有此Function,即使它们未定义iter()方法)。

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

该字段由子类型继承。

  • iternextfunc PyTypeObject.tp_iternext
    • 指向函数的可选指针,该函数返回迭代器中的下一项。当迭代器用尽时,它必须返回* NULL ;可以设置StopIterationexception,也可以不设置。当发生另一个错误时,它也必须返回 NULL *。它的存在通常表明该类型的实例是迭代器(尽管经典实例始终具有此Function,即使它们没有定义next()方法)。

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

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

该字段由子类型继承。

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

  • 结构PyMethodDef * PyTypeObject.tp_methods
    • 指向以 NULL 终止的PyMethodDef结构的静态数组的可选指针,语句此类型的常规方法。

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

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

  • 结构PyMemberDef * PyTypeObject.tp_members
    • 指向由PyMemberDef结构终止的静态* NULL *终止数组的可选指针,语句此类型实例的常规数据成员(字段或插槽)。

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

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

  • 结构PyGetSetDef * PyTypeObject.tp_getset
    • 指向由_结构终止的静态* NULL *终止数组的可选指针,语句此类型实例的计算属性。

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

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

  • PyTypeObject* PyTypeObject.tp_base
    • 指向从其继承类型属性的基本类型的可选指针。在此级别上,仅支持单继承。多重继承要求pass调用元类型来动态创建类型对象。

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

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

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

  • descrgetfunc PyTypeObject.tp_descr_get
    • 指向“Descriptors 获取”Function的可选指针。

Function签名是

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

该字段由子类型继承。

  • descrsetfunc PyTypeObject.tp_descr_set
    • 指向用于设置和删除 Descriptors 值的函数的可选指针。

Function签名是

int tp_descr_set(PyObject *self, PyObject *obj, PyObject *value);
  • value 参数设置为 NULL *以删除该值。该字段由子类型继承。
  • PyTypeObject.tp_dictoffset
    • 如果此类型的实例具有包含实例变量的字典,则此字段为非零值,并且包含实例变量字典类型的实例中的偏移量;否则为 0. PyObject_GenericGetAttr()使用此偏移量。

不要将此字段与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。)

  • initproc PyTypeObject.tp_init
    • 实例初始化函数的可选指针。

此函数对应于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 *的话。)

该字段由子类型继承。

  • allocfunc PyTypeObject.tp_alloc
    • 指向实例分配Function的可选指针。

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(),以强制执行标准堆分配策略。这也是静态定义类型的建议值。

  • newfunc PyTypeObject.tp_new
    • 指向实例创建函数的可选指针。

如果该函数对于特定类型为* 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 链接就不能被调用。

  • 析构函数PyTypeObject.tp_free
    • 实例释放函数的可选指针。

该函数的签名已稍有变化:在 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标志位的值匹配的解除分配器。

  • inquiry PyTypeObject.tp_is_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 和更高版本中被继承.)

  • PyObject* PyTypeObject.tp_bases
    • 基本类型的 Tuples。

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

此字段未继承。

  • PyObject* PyTypeObject.tp_mro
    • 在方法解析 Sequences 中,Tuples 包含扩展的基本类型集,从基本类型本身开始,以object结尾。

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

  • PyObject* PyTypeObject.tp_cache

    • 没用过。不继承。仅供内部使用。
  • PyObject* PyTypeObject.tp_subclasses

    • 对子类的弱引用列表。不继承。仅供内部使用。
  • PyObject* PyTypeObject.tp_weaklist

    • 弱引用列表头,用于对此类型对象的弱引用。不继承。仅供内部使用。

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

  • Py_ssize_t PyTypeObject.tp_allocs

    • 分配数。
  • Py_ssize_t PyTypeObject.tp_frees

    • 免费数量。
  • Py_ssize_t PyTypeObject.tp_maxalloc

    • 同时分配的最大对象数。
  • PyTypeObject* PyTypeObject.tp_next

    • 指向带有非零tp_allocs字段的下一个类型对象的指针。

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

数字对象结构

  • PyNumberMethods
    • 此结构包含指向对象用来实现数字协议的Function的指针。 Number Protocol部分中记录的类似名称的Function使用下面的几乎每个Function。

这是结构定义:

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_TPFLAGS_CHECKTYPES,则保证函数参数为对象的类型;调用者负责调用nb_coerce成员指定的强制方法来转换参数:

  • 强制PyNumberMethods.nb_coerce

    • PyNumber_CoerceEx()使用此Function,并且具有相同的签名。第一个参数始终是指向已定义类型的对象的指针。如果可以转换为常见的“较大”类型,则该函数将指针替换为对转换后的对象的新引用,并返回0。如果无法进行转换,则该函数返回1。如果设置了错误条件,它将返回-1
  • 如果设置了Py_TPFLAGS_CHECKTYPES标志,则二进制和三进制函数必须检查其所有操作数的类型,并实现必要的转换(至少一个操作数是已定义类型的实例)。这是推荐的方法;使用 Python 3 强制将完全消失。

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

Map 对象结构

  • PyMappingMethods

    • 此结构保存指向对象用来实现 Map 协议的Function的指针。它具有三个成员:
  • lenfunc PyMappingMethods.mp_length

  • binaryfunc PyMappingMethods.mp_subscript

  • objobjargproc PyMappingMethods.mp_ass_subscript

序列对象结构

  • PySequenceMethods

    • 此结构保存指向对象用来实现序列协议的Function的指针。
  • lenfunc PySequenceMethods.sq_length

  • binaryfunc PySequenceMethods.sq_concat

    • PySequence_Concat()使用此Function,并且具有相同的签名。在trypassnb_add插槽进行数字加法运算后,+运算符也使用它。
  • ssizeargfunc PySequenceMethods.sq_repeat

    • PySequence_Repeat()使用此Function,并且具有相同的签名。在trypassnb_multiply插槽进行数值乘法之后,*运算符也使用它。
  • ssizeargfunc PySequenceMethods.sq_item

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

  • ssizeobjargproc PySequenceMethods.sq_ass_item

    • PySequence_SetItem()使用此Function,并且具有相同的签名。如果对象不支持项目分配和删除,则该插槽可以留为* NULL *。
  • objobjproc PySequenceMethods.sq_contains

  • binaryfunc PySequenceMethods.sq_inplace_concat

    • PySequence_InPlaceConcat()使用此Function,并且具有相同的签名。它应该修改其第一个操作数,然后将其返回。
  • ssizeargfunc PySequenceMethods.sq_inplace_repeat

    • PySequence_InPlaceRepeat()使用此Function,并且具有相同的签名。它应该修改其第一个操作数,然后将其返回。

缓冲区对象结构

缓冲区接口导出一个模型,在该模型中,对象可以将其内部数据公开为一组数据块,其中每个块均指定为指针/长度对。这些块称为* 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 解释器需要能够在使用前测试其存在性。

  • PyBufferProcs
    • 用于保存定义缓冲协议实现的Function指针的结构。

第一个插槽是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 *个字符。

  • Py_TPFLAGS_HAVE_GETCHARBUFFER

    • 在类型结构中设置标志位以指示bf_getcharbuffer插槽是已知的。设置此值并不表示该对象支持缓冲区接口,也不表示bf_getcharbuffer插槽不是* NULL *。
  • Py_ssize_t (*readbufferproc)(PyObject **self ,Py_ssize_t * segment ,void * ptrptr *)

    • 返回指向*ptrptr中缓冲区的可读段的指针。允许此函数引发异常,在这种情况下,它必须返回-1。指定的* segment *必须为零或正数,并且严格小于bf_getsegcount slot 函数返回的段数。成功时,它将返回段的长度,并将*ptrptr设置为指向该内存的指针。
  • Py_ssize_t (*writebufferproc)(PyObject **self ,Py_ssize_t * segment ,void * ptrptr *)

    • 返回指向*ptrptr中可写内存缓冲区的指针,并将该段的长度作为函数返回值。内存缓冲区必须对应于缓冲区段* segment 。必须返回-1并在错误时设置异常。如果对象仅支持只读缓冲区,则应引发TypeError;而当 segment *指定不存在的段时,应引发SystemError
  • Py_ssize_t (*segcountproc)(PyObject *self ,Py_ssize_t len *)

    • 返回组成缓冲区的内存段数。如果* lenp 不是 NULL *,则实现必须报告*lenp中所有段的大小总和(以字节为单位)。该Function不会失败。
  • Py_ssize_t (*charbufferproc)(PyObject **self ,Py_ssize_t * segment ,char * ptrptr *)

    • 返回* ptrptr 设置为的段 segment *的大小。 *ptrptr设置为内存缓冲区。错误返回-1