类型对象

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

与大多数标准类型相比,类型对象相当大。大小的原因是每个类型对象存储大量值,主要是Cfunction指针,每个指针都实现了类型功能的一小部分。本节详细介绍了类型对象的字段。这些字段将按照它们在结构中的顺序进行描述.

Typedef:unaryfunc,binaryfunc,ternaryfunc,inquiry,intargfunc,intintargfunc,intobjargproc,intintobjargproc,objobjargproc,destructor,freefunc,printfunc,getattrfunc,getattrofunc,setattrfunc,setattrofunc,reprfunc,hashfunc

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

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

    /* Methods to implement standard operations */

    destructor tp_dealloc;
    printfunc tp_print;
    getattrfunc tp_getattr;
    setattrfunc tp_setattr;
    PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
                                    or tp_reserved (Python 3) */
    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 */
    unsigned long tp_flags;

    const char *tp_doc; /* Documentation string */

    /* call function for all accessible objects */
    traverseproc tp_traverse;

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

    /* rich comparisons */
    richcmpfunc tp_richcompare;

    /* weak reference enabler */
    Py_ssize_t tp_weaklistoffset;

    /* 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;
    Py_ssize_t 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;
    destructor tp_del;

    /* Type attribute cache version tag. Added in version 2.6 */
    unsigned int tp_version_tag;

    destructor tp_finalize;

} PyTypeObject;

类型对象结构扩展PyVarObject结构体。该ob_sizefield用于动态类型(由type_new()创建,通常从类语句调用)。注意PyType_Type(themetatype)初始化tp_itemsize,这意味着它的实例(即类型对象mustob_size field.

PyObject* PyObject._ob_next
PyObject * PyObject._ob_prev
这些字段仅在定义宏Py_TRACE_REFS时出现。初始化为NULLPyObject_HEAD_INIT宏。对于静态分配的对象,这些字段始终保持为NULL。对于动态分配的对象,这两个字段用于将对象链接到all堆上的活对象。这可以用于各种调试目的;目前唯一的用途是在环境变量PYTHONDUMPREFS设置了

这些字段不是由子类型继承的.

Py_ssize_t PyObject.ob_refcnt
这是类型对象的引用计数,初始化为1通过PyObject_HEAD_INIT宏。请注意,对于静态分配的类型对象,类型的实例ob_type指向该类型的对象)执行not算作参考。但对于动态分配的类型对象,实例do算作引用

这个字段不是由子类型继承的.

PyTypeObject * PyObject.ob_type
这是类型的类型,换句话说就是它的元类型。它由PyObject_HEAD_INIT宏的参数初始化,其值通常应为&PyType_Type。但是,对于必须在Windows上可用的动态可加载扩展模块(至少),编译器会抱怨这不是有效的初始化程序。因此,惯例是通过NULLPyObject_HEAD_INIT宏并在执行任何其他操作之前,在模块初始化函数的开头显式初始化该字段。这通常是这样做的:

Foo_Type.ob_type = &PyType_Type;

这应该在创建任何类型实例之前完成.PyType_Ready()检查是否ob_typeNULL,如果是,则将其初始化为ob_type字段基类的.PyType_Ready()如果非零,则不会更改此字段.

该字段由子类型继承.

Py_ssize_t PyVarObject.ob_size
对于静态分配的类型对象,应将其初始化为零。福特动力分配型物体,这个场具有特殊的内在意义.

这个字段不是由子类型继承的.

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

对于动态分配的类型对象,这应该只是类型名称,并且模式名称显式存储在类型dict中作为键的值"__module__".

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

如果没有点,整个tp_name字段可以访问__name__属性,和__module__属性未定义(除非在字典中明确设置,如上所述)。这意味着yourtype将无法腌制。另外,它不会列在用pydoc创建的模块文件中

这个字段不是由子类型继承的.

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

有两种类型:具有固定长度实例的类型具有零tp_itemsize字段,具有可变长度实例的类型具有非类型-zero tp_itemsize场。对于具有固定长度实例的类型,allinstances具有相同的大小,在tp_basicsize.

中给出对于具有可变长度实例的类型,实例必须具有ob_size字段,并且实例大小为tp_basicsize加上N次tp_itemsize,其中N是物体的“长度”。N的值通常存储在实例的ob_size字段中。有一些例外:例如,int使用负数ob_size表示负数,N是abs(ob_size)那里。此外,实例布局中存在ob_size字段并不意味着实例结构是可变长度的(例如,列表类型的结构具有固定长度的实例,但这些实例具有有意义的ob_sizefield).

基本大小包括宏PyObject_HEADPyObject_VAR_HEAD声明的实例中的字段(无论哪个用于声明实例结构)和如果它们存在,则反过来包括_ob_prev_ob_next字段。这意味着获取tp_basicsize初始值设定项的唯一正确途径是在用于声明实例布局的结构上使用sizeof运算符。基本大小不包括GC头大小。

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

关于对齐的注释:如果变量项需要特定的对齐,则应该使用tp_basicsize的值来处理。示例:假设一个类型实现double. tp_itemsize的数组是sizeof(double)。程序员有责任tp_basicsizesizeof(double)的倍数(假设这是double的对齐要求.

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

析构函数Py_DECREF()Py_XDECREF()新引用计数为零时的宏。此时,实例仍然存在,但没有引用它。析构函数应释放实例拥有的所有引用,释放实例拥有的所有内存缓冲区(使用与用于分配缓冲区的分配函数对应的释放函数),最后(作为其行为)调用类型的tp_free功能。如果类型不是subable(没有设置Py_TPFLAGS_BASETYPE标志位),则允许直接调用对象解除分配器而不是通过tp_free。对象解除分配器应该是用于分配实例的对象;这通常是PyObject_Del()如果实例被分配使用PyObject_New()PyObject_VarNew(),或PyObject_GC_Del()如果实例是使用PyObject_GC_New()PyObject_GC_NewVar().

此字段由子类型继承.

printfunc PyTypeObject.tp_print
保留的插槽,以前用于Python 2.x.
getattrfunc中的打印格式PyTypeObject.tp_getattr
指向get-attribute-string函数的可选指针.

该字段已弃用。当它被定义时,它应该指向一个与tp_getattro函数相同的函数,但是使用C字符串而不是Python字符串对象来给出属性名称。签名是

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

这个字段是由子类型tp_getattro一起继承的:一个子类型从它的基类型tp_getattrtp_getattro同时包含子类型tp_getattrtp_getattro都是NULL.

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

不推荐使用此字段。当它被定义时,它应该指向一个与tp_setattro函数相同的函数,但是使用C字符串而不是Python字符串对象来给出属性名称。签名是

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

v参数设置为NULL删除属性。该字段由子类型tp_setattro一起继承:子类型同时tp_setattrtp_setattro从它的基本类型开始,子类的tp_setattrtp_setattro都是NULL.

PyAsyncMethods * tp_as_async
指向另一个结构的指针,该结构包含仅与实现等待异步迭代器 C级协议的对象相关的字段。请参阅异步对象结构了解详情.

新版本3.5:以前称为tp_comparetp_reserved.

reprfunc PyTypeObject.tp_repr

指向实现内置函数函数的可选指针repr().

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

这个字段未设置,形式为<%s object at %p>的字符串返回,其中%s由类型名称替换,而%p由对象的存储地址替换.

该字段由子类型继承.

PyNumberMethods * tp_as_number
指向另一个结构的指针,该结构包含仅与实现数字协议的对象相关的字段。这些字段记录在数字对象结构中.

tp_as_number字段不是继承的,但包含的字段是单独的.

PySequenceMethods* tp_as_sequence
指向另一个结构的指针,该结构包含仅与实现序列协议的对象相关的字段。这些字段记录在序列对象结构中.

tp_as_sequence字段不是继承的,但是包含的字段是单独继承的.

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

tp_as_mapping字段不是继承的,但包含的字段是单独继承的.

hashfunc PyTypeObject.tp_hash

指向实现内置函数的函数的可选指针hash().

签名与PyObject_Hash()相同;它必须返回Py_hash_t类型的avalue。价值 -1不应该作为正常返回值返回;当在计算hashvalue期间发生错误时,该函数应设置异常并返回-1.

此字段可以显式设置为PyObject_HashNotImplemented()来自父类型的哈希方法的toblock继承。这在Python级别被解释为__hash__ = None,导致isinstance(o, collections.Hashable)正确地返回False。注意反过来也是如此 – 在Python级别的类上设置__hash__ = None会导致tp_hash槽设置为PyObject_HashNotImplemented().

如果未设置此字段,则尝试获取对象的哈希值TypeError.

此字段由子类型与tp_richcompare一起继承:子类型继承tp_richcomparetp_hash,当子类型的tp_richcomparetp_hash都是NULL.

ternaryfunc PyTypeObject.tp_call
指向实现调用对象的函数的可选指针。这应该是NULL如果对象不可调用。签名与PyObject_Call().

相同。该字段由子类型继承.

reprfunc PyTypeObject.tp_str
指向实现内置操作的函数的可选指针str()。(注意str现在是一个类型,str()调用该类型的构造函数。这个构造函数调用PyObject_Str()来实际工作,PyObject_Str()会调用这个处理程序。)

签名与PyObject_Str()相同;它必须返回一个字符串或一个Unicode对象。这个函数应该返回一个对象的“友好”字符表示,因为这是由print()函数

使用的表示,当这个字段没有设置时,PyObject_Repr()被调用来返回一个stringrepresentation.

这个字段是由子类型继承的.

getattrofunc PyTypeObject.tp_getattro
一个指向get-attribute函数的可选指针.

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

这个字段是由子类型和tp_getattr一起继承的:一个子类型同时包含tp_getattrtp_getattro从它的基类型开始,子类的tp_getattrtp_getattro都是NULL.

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

签名与PyObject_SetAttr()相同,但必须支持将v设置为NULL以删除属性。将此字段设置为PyObject_GenericSetAttr(),它实现了设置对象属性的常规方式.

这个字段是由子类型和tp_setattr一起继承的:一个子类型同时包含tp_setattrtp_setattro从它的基本类型来看,子类型tp_setattrtp_setattro都是NULL.

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

tp_as_buffer字段不是继承的,但是包含的字段是单独的.

长的PyTypeObject.tp_flags
该字段是各种标志的位掩码。某些标志表示某些情况下的变体语义;其他用于表示类型对象中的某些字段(或通过引用的扩展结构)tp_as_number, tp_as_sequence, tp_as_mapping,和tp_as_buffer)历史上并不总是存在是有效的;如果一个标志位是清楚的,它的防护类型字段一定不能被访问,并且必须被认为是零或NULL价值相反.

这个领域的继承很复杂。大多数标志位是单独继承的,即如果基类型设置了标志位,则子类型继承该标志位。如果扩展结构是继承的,则与扩展结构有关的标志位是严格的,即标志位的基本类型的值与指向扩展结构的指针一起被复制到子类型中。Py_TPFLAGS_HAVE_GC标志位与tp_traversetp_clear字段一起继承,即如果Py_TPFLAGS_HAVE_GC标志位在子类型中清除,则tp_traversetp_clear子类型中的字段存在且有NULL values.

目前定义了以下位掩码;这些可以使用|运算符进行OR运算,以形成tp_flags字段的值。宏PyType_HasFeature()取一个类型和一个标志值,tpf,并检查是否tp->tp_flags & f是非零的

Py_TPFLAGS_HEAPTYPE
在堆上分配类型对象本身时设置此位。在这个案例中,ob_type实例的字段被视为对类型的引用,并且类型对象在创建新实例时为INCREF,在实例被销毁时为DECREF(这不适用于子类型的实例;仅实例引用的类型)ob_type获得INCREF’ed或DECREF’ed).
Py_TPFLAGS_BASETYPE
当类型可以用作另一种类型的基本类型时,该位置位。如果这个位是清楚的,那么类型不能被子类型化(类似于Java中的“最终”类).
Py_TPFLAGS_READY
PyType_Ready().
Py_TPFLAGS_READYING
完全初始化类型对象时设置此位。在PyType_Ready()正在初始化类型对象的过程中设置此位.
Py_TPFLAGS_HAVE_GC
当对象支持垃圾回收时设置此位。如果设置了这个位,则必须使用PyObject_GC_New()创建实例并使用PyObject_GC_Del()创建实例。更多信息,请参阅支持循环垃圾收集。这一点也暗示了与GB相关的字段tp_traversetp_clear存在于类型对象中.
Py_TPFLAGS_DEFAULT
这是与类型对象及其扩展结构中某些字段的存在有关的所有位的位掩码。目前,它包括以下几个部分:Py_TPFLAGS_HAVE_STACKLESS_EXTENSION,Py_TPFLAGS_HAVE_VERSION_TAG.
Py_TPFLAGS_LONG_SUBCLASS
Py_TPFLAGS_LIST_SUBCLASS
Py_TPFLAGS_TUPLE_SUBCLASS
Py_TPFLAGS_BYTES_SUBCLASS
Py_TPFLAGS_UNICODE_SUBCLASS
Py_TPFLAGS_DICT_SUBCLASS
Py_TPFLAGS_BASE_EXC_SUBCLASS
Py_TPFLAGS_TYPE_SUBCLASS
这些标志由诸如PyLong_Check()之类的函数使用,以快速确定类型是否是内置类型的子类;这样的特定检查比通用检查更快,比如PyObject_IsInstance()。从内置函数继承的自定义类型应该有tp_flags适当设置,或者与这些类型交互的代码将根据使用的检查类型而有所不同.
Py_TPFLAGS_HAVE_FINALIZE
当在结构中存在tp_finalize插槽时,此位置位.

版本3.4.

const中的新内容char * PyTypeObject.tp_doc
指向NUL终止的C字符串的可选指针,为thistype对象提供docstring。这个类型和类型的__doc__属性被公开了

这个字段是not由子类型继承

traverseproc PyTypeObject.tp_traverse
指向垃圾收集器的遍历函数的可选指针。如果设置了Py_TPFLAGS_HAVE_GC标志位,则仅使用此方法。有关Python垃圾收集方案的更多信息,请参阅支持循环垃圾收集.

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

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()需要visitarg参数来local_traverse()才能拥有这些特定名称;不要把它们命名为任何东西.

这个领域与tp_clearPy_TPFLAGS_HAVE_GC标志位:标志位,tp_traversetp_clear都是从基类型继承的,如果它们在子类型中全为零.

inquiry PyTypeObject.tp_clear
指向垃圾收集器的清除函数的可选指针。这只适用于Py_TPFLAGS_HAVE_GC标志位设置.

tp_clear成员函数用于打破垃圾收集器检测到的循环垃圾中的参考周期。总而言之,tp_clear系统中的函数必须组合以打破所有参考周期。这个问题,如果有任何疑问,提供tp_clear功能。例如,元组类型没有实现tp_clear函数,因为它可以证明没有参考周期可以完全由元组组成。因此tp_clear其他类型的函数必须足以破坏包含元组的任何循环。这并不是很明显,而且有一个很好的理由可以避免实施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之后,不能递减对包含对象的引用。这是因为减少引用计数可能会导致包含的对象变为垃圾,从而触发一系列回收活动,其中可能包括调用任意游戏代码(由于终结符或与包含对象关联的弱反回调)。如果这样的代码可能再次引用self,那么指向包含对象的指针在那时是NULL是很重要的,这样self知道包含的对象不能更长时间使用。Py_CLEAR()宏按安全顺序执行操作.

因为tp_clear函数是打破引用循环,没有必要清除包含的对象,如Python字符串或Pythonintegers,它们不能参与引用循环。另一方面,清除所有包含的Python对象并编写类型tp_dealloc调用tp_clear.

关于Python的垃圾收集方案的更多信息可以在@找到支持循环垃圾收集.

该字段由子类型tp_traverse以及Py_TPFLAGS_HAVE_GC标志位继承:标志位,tp_traversetp_clear都是从基类型继承而来的如果他们在子类型中都是零.

richcmpfunc PyTypeObject.tp_richcompare
指向富比较函数的可选指针,其签名为PyObject *tp_richcompare(PyObject *a, PyObject *b, int op)。第一个参数保证是由PyTypeObject.

函数应该返回比较结果(通常是Py_TruePy_False)。如果比较未定义,则必须返回Py_NotImplemented,如果发生另一个错误则必须返回NULL并设置例外情况.

注意

如果你想实现一种只有一组有限的比较有意义的类型(例如==!=, 但不是 <andfriends),直接举起TypeError在丰富的比较功能中

该字段由子类型和tp_hash:子类继承tp_richcomparetp_hash当子类tp_richcomparetp_hashNULL.

以下常量被定义为tp_richcompare的第三个参数和PyObject_RichCompare()

常量 比较
Py_LT <
Py_LE <=
Py_EQ ==
Py_NE !=
Py_GT >
Py_GE >=

定义下面的宏来简化编写丰富的比较函数:

PyObject * Py_RETURN_RICHCOMPARE VAL_A,VAL_B,int  op
返回Py_TruePy_False从函数中,取决于比较的结果.VAL_A和VAL_B必须由C比较运算符订购(例如,它们可以是C int或浮点数)。第三个参数指定了所请求的操作,如PyObject_RichCompare().

返回值的引用计数正确递增.

出错时,设置异常并从函数返回NULL。

在版本3.7.

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

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

该字段由子类型继承,但请参阅下面列出的规则。子类型可以覆盖此偏移量;这意味着子类型使用与基本类型不同的弱引用列表头。由于列表头总是通过tp_weaklistoffset找到,这应该不是问题.

当类声明定义的类型没有__slots__声明,并且它的基本类型都不是弱引用的,通过向实例布局添加弱引用列表头槽并设置该槽的偏移量tp_weaklistoffset来使类型弱引用.

type的__slots__声明包含一个名为__weakref__的插槽,该插槽成为该类型实例的弱引用列表头,并且插槽的偏移量存储在类型的tp_weaklistoffset.

中当一个类型的__slots__声明不包含名为__weakref__的插槽,该类型从其基类型继承其tp_weaklistoffset.

getiterfunc PyTypeObject.tp_iter
可选指针返回一个返回对象迭代器的函数。它的存在通常表示这种类型的实例是可迭代的(虽然序列可以在没有这个函数的情况下迭代).

这个函数和PyObject_GetIter().

具有相同的签名。这个字段由子类型继承.

iternextfunc PyTypeObject.tp_iternext
一个指向函数的可选指针,它返回迭代器中的下一个项。当迭代器耗尽时,它必须返回NULL;一个 StopIterationexception可能设置也可能不设置。当发生另一个错误时,它必须返回NULL太。它的存在表明这种类型的实例是atiterators

演员类型也应该定义tp_iter函数,该函数应返回迭代器实例本身(不是新的iteratorinstance).

此功能与PyIter_Next().

该字段由子类型继承.

struct PyMethodDef * PyTypeObject.tp_methods
指向静态NULL的可选指针 – PyMethodDef结构的数组,声明这种类型的常规方法.

对于数组中的每个条目,都会在类型的字典中添加一个条目(参见下面的tp_dict),其中包含一个方法描述符.

这个字段不是由子类型继承的(方法是通过不同的机制继承的).

struct PyMemberDef * PyTypeObject.tp_members
一个指向静态NULL– 的终止数组PyMemberDef结构,声明此类型实例的常规数据成员(字段或槽).

对于数组中的每个条目,都会在类型的字典中添加一个条目(请参阅tp_dict下面)包含一个成员描述符.

这个字段不是由子类型继承的(成员是通过不同的机制继承的).

struct PyGetSetDef * PyTypeObject.tp_getset
一个指向静态NULL的可选指针 – PyGetSetDef结构的数组,声明此类实例的计算属性.

对于数组中的每个条目,都会在类型的字典中添加一个条目(参见下面的tp_dict),其中包含一个getset描述符.

该字段不是由子类型继承的(计算属性通过不同的机制继承).

PyTypeObject * PyTypeObject.tp_base
指向从中继承类型属性的基类型的可选指针。在这个级别,只支持单继承;需要通过调用metatype来动态创建一个类型对象的多重继承.

这个字段不是由子类型继承的(很明显),但是它默认为&PyBaseObject_Type(对于Python程序员来说,它被称为类型object).

PyObject * PyTypeObject.tp_dict
类型的字典由PyType_Ready().

在调用PyType_Ready之前,通常应该将该字段初始化为NULL;它也可以初始化为包含该类型的初始属性的字典。一次PyType_Ready()已经初始化了类型,只有当它们没有对应于重载操作(如__add__()).

这个字段不是由子类型继承的(尽管这里定义的属性是通过不同的机制继承的).

警告

使用PyDict_SetItem()或者修改是不安全的tp_dict用词典C-API.

descrgetfunc PyTypeObject.tp_descr_get
指向“描述符获取”功能的可选指针.

功能签名是

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

这个字段是由subtypes继承.

descrsetfunc PyTypeObject.tp_descr_set
一个指向设置和删除描述符值的函数的可选指针.

函数签名是

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

value参数设置为NULL删除该值。该字段由子类型继承.

Py_ssize_t PyTypeObject.tp_dictoffset
如果此类型实例具有包含实例变量的字典,则此字段为非零且包含实例变量字典类型的实例;这个偏移用于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_basicsize, tp_itemsizetp_dictoffset类型对象中获取,并且ob_size取自实例。绝对值是因为int使用ob_size的符号来表示数字的符号。(从来没有必要自己做这个计算;它是由_PyObject_GetDictPtr().)

这个字段由子类型继承,但请参阅下面列出的规则。子类型可以覆盖此偏移量;这意味着子类型实例将字典存储在与基本类型不同的偏移量处。由于字典总是通过tp_dictoffset找到,这应该不是问题.

当类语句定义的类型没有__slots__声明,并且它的基类型都没有实例变量字典时,dictionaryslot被添加到实例布局中,而tp_dictoffset被设置为插槽的offset.

当一个类声明定义的类型有一个__slots__声明时,该类型从它的基类型继承它的tp_dictoffset.

(添加一个名为__dict__的插槽到__slots__声明没有预期的效果,它只会造成混乱。也许这应该像__weakref__那样加入一个特征。)

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

这个函数对应于__init__()类的方法。像__init__()一样,可以在不调用__init__()的情况下创建实例,并且可以通过再次调用__init__()方法来重新初始化实例.

函数签名是

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

self参数是要初始化的实例;argskwds参数表示调用__init__().

的位置和关键字参数tp_init函数,如果不是NULL,在通常通过调用其类型创建实例时调用,类型tp_newfunctionhas返回了该类型实例。如果tp_new函数返回的某个其他类型的实例不是原始类型的子类型,则不调用tp_init函数;如果tp_new返回原始类型的子类型的实例,子类型的tp_init叫做。

该字段由子类型继承.

allocfunc PyTypeObject.tp_alloc
指向实例分配函数的可选指针.

函数签名是

PyObject *tp_alloc(PyTypeObject *self, Py_ssize_t nitems)

函数的目的是将内存分配与内存初始化分开。它应该返回一个指向实例的足够长度的内存块的指针,适当地对齐,并初始化为零,但是ob_refcnt设置为1ob_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,则该类型不能被称为创建新实例;可能还有其他一些创建实例的方法,比如工厂函数.

函数签名是

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

类型参数是正在创建的对象类型;argskwdsarguments表示对type的调用的位置和关键字参数。注意,子类型不必等于调用tp_new函数的类型;它可能是该类型的子类型(但不是不相关的类型).

tp_new函数应调用subtype->tp_alloc(subtype, nitems)对象分配空间,然后只做同样的事情进一步初始化是绝对必要的。可以安全地签名或重复的初始化应放在tp_init处理程序中。一个好的例子是,对于不可变类型,所有初始化都应该在tp_new中进行,而对于可变类型,大多数初始化应该被推迟到tp_init.

这个字段由子类型继承,除非它不是继承的通过静态类型tp_baseNULL&PyBaseObject_Type.

析构函数PyTypeObject.tp_free
指向实例释放函数的可选指针。它的签名是freefunc

void tp_free(void *)

与此签名兼容的初始化程序是PyObject_Free().

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

//询问PyTypeObject.tp_is_gc
指向垃圾收集器调用的函数的可选指针.

垃圾收集器需要知道特定对象是否可收集。通常情况下,查看对象的类型tp_flags字段就足够了,检查Py_TPFLAGS_HAVE_GC标志位。但是有些类型具有静态和动态分配的实例,并且静态分配的实例不可收集。这种类型应该定义这个功能;它应该返回1对于可收集的实例,和0用于不可收集的实例。签名是

int tp_is_gc(PyObject *self)

(唯一的例子就是类型本身。元类型PyType_Type,定义此函数以区分静态和动态分配的类型。)

此字段由子类型继承.

PyObject * PyTypeObject.tp_bases
基类型的元组.

这是为类语句创建的类型设置的。它应该是NULL forstatically定义类型

这个字段不是继承的.

PyObject* PyTypeObject.tp_mro
包含扩展的基类型集的元组,从头开始类型本身,以object结尾,在方法解析顺序中

这个字段不是继承的;它是由PyType_Ready().

析构函数PyTypeObject.tp_finalize
计算的新的实例终结函数的可选指针。它的签名是destructor

void tp_finalize(PyObject *)

如果设置了tp_finalize,解释器在最终确定实例时会调用它一次。它可以从garbagecollector(如果实例是隔离的引用循环的一部分)调用,也可以在释放对象之前调用。无论哪种方式,都可以保证在尝试打破引用周期之前调用它,确保它找到一个理智状态的对象.

tp_finalize不应该改变当前的异常状态;因此,建议的方法是写一个非平凡的终结者是:

static void
local_finalize(PyObject *self)
{
    PyObject *error_type, *error_value, *error_traceback;

    /* Save the current exception, if any. */
    PyErr_Fetch(&error_type, &error_value, &error_traceback);

    /* ... */

    /* Restore the saved exception. */
    PyErr_Restore(error_type, error_value, error_traceback);
}

要考虑这个字段(即使通过继承),你还必须设置Py_TPFLAGS_HAVE_FINALIZE flags bit.

这个字段是由子类型继承的.

新版本3.4.

参见

“安全对象终结”( PEP 442

PyObject * PyTypeObject.tp_cache
未使用。没有继承。仅限内部使用.
PyObject* PyTypeObject.tp_subclasses
子类的弱引用列表。没有继承。仅供内部使用.
PyObject* PyTypeObject.tp_weaklist
弱引用列表头,用于对此类型对象的弱引用。Notinherited。限内部使用。

仅在功能测试宏COUNT_ALLOCS是定义的,仅供内部使用。为了完整起见,这里记录了它们。这些字段都不是由子类继承的.

Py_ssize_t PyTypeObject.tp_allocs
分配数量.
Py_ssize_t PyTypeObject.tp_frees
frees的数量.
Py_ssize_t PyTypeObject.tp_maxalloc
最大同时分配的对象.
PyTypeObject * PyTypeObject.tp_next
指向非零的下一个类型对象tp_allocs field.

另请注意,在垃圾收集的Python中,tp_dealloc可能被称为fromany Python线程,而不仅仅是创建对象的线程(如果对象成为引用计数循环的一部分,则该循环可能由任何线程上的garbagecollection收集)。对于Python API调用,这不是问题,因为调用tp_dealloc的线程将拥有全局解释器锁(GIL)。但是,如果被销毁的对象反过来从另一个C或C ++库中销毁对象,应该注意确保在调用tp_dealloc的线程上销毁那些对象不会违反库的任何假设.

&nbsp;

Number对象结构

PyNumberMethods
此结构保存指向对象用于实现数字协议的函数指针。每个函数都由数字协议 section

中记载的类似名称的功能使用。这里是结构定义:

typedef struct {
     binaryfunc nb_add;
     binaryfunc nb_subtract;
     binaryfunc nb_multiply;
     binaryfunc nb_remainder;
     binaryfunc nb_divmod;
     ternaryfunc nb_power;
     unaryfunc nb_negative;
     unaryfunc nb_positive;
     unaryfunc nb_absolute;
     inquiry nb_bool;
     unaryfunc nb_invert;
     binaryfunc nb_lshift;
     binaryfunc nb_rshift;
     binaryfunc nb_and;
     binaryfunc nb_xor;
     binaryfunc nb_or;
     unaryfunc nb_int;
     void *nb_reserved;
     unaryfunc nb_float;

     binaryfunc nb_inplace_add;
     binaryfunc nb_inplace_subtract;
     binaryfunc nb_inplace_multiply;
     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;

     binaryfunc nb_floor_divide;
     binaryfunc nb_true_divide;
     binaryfunc nb_inplace_floor_divide;
     binaryfunc nb_inplace_true_divide;

     unaryfunc nb_index;

     binaryfunc nb_matrix_multiply;
     binaryfunc nb_inplace_matrix_multiply;
} PyNumberMethods;

注意

二进制和三进制函数必须检查其所有操作数的类型,并实现必要的转换(至少有一个操作数是已定义类型实例)。如果没有为给定的操作数定义操作,则二进制和三进制函数必须返回Py_NotImplemented,如果发生了另一个错误,他们必须返回NULL并设置例外.

注意

nb_reserved字段应该始终是NULL。以前称为nb_long,并在Python 3.0.1中重命名.

&nbsp;

映射对象结构

PyMappingMethods
此结构包含指向对象用于实现映射协议的函数指针。它有三个成员:
lenfunc PyMappingMethods.mp_length
//功能由PyMapping_Size()PyObject_Size(),并具有相同的签名。此插槽可设置为NULL如果物体没有明确的长度
binaryfunc PyMappingMethods.mp_subscript
功能由使用PyObject_GetItem()PySequence_GetSlice(),和PyObject_GetItem()具有相同的签名。这个槽必须填入PyMapping_Check()函数才能返回1,它可以是NULLotherwise.
objobjargproc PyMappingMethods.mp_ass_subscript
这个函数是用于PyObject_SetItem(),PyObject_DelItem(), PyObject_SetSlice()PyObject_DelSlice()。它与PyObject_SetItem()具有相同的签名,但是v也可以设置为NULL来删除项目。如果这个插槽是NULL,则该对象不支持itemassignment和deletion.

 

序列对象结构

PySequenceMethods
这个结构保存了一个对象用于实现序列协议的函数的指针.
lenfunc PySequenceMethods.sq_length
这个函数被PySequence_Size()使用PyObject_Size(),并具有相同的签名。它还用于通过sq_itemsq_ass_item slots.
binaryfunc PySequenceMethods.sq_concat
来处理负索引。//功能由PySequence_Concat()并具有相同的标志。它也被+操作员,通过nb_addslot.
ssizeargfunc PySequenceMethods.sq_repeat
功能由使用PySequence_Repeat()并具有相同的标志。它也被*操作符,在通过nb_multiplyslot.
ssizeargfunc PySequenceMethods.sq_item
功能由使用PySequence_GetItem()并具有相同的标志。通过PyObject_GetItem()插槽尝试订阅后,mp_subscript也使用它。这个插槽必须填入PySequence_Check()函数才能返回1,它可以是NULL否则.

负索引处理如下:如果sq_lengthslot isfilled,调用它,序列长度用于计算传递给sq_item。如果sq_lengthNULL,索引按原样传递给函数.

ssizeobjargproc PySequenceMethods.sq_ass_item
这个函数由PySequence_SetItem()使用并具有相同的签名。它也被PyObject_SetItem()PyObject_DelItem(),在通过mp_ass_subscript slot尝试项目分配和删除后。如果对象不支持分配和删除,则该槽可以留给NULL
objobjproc PySequenceMethods.sq_contains
该功能可以由PySequence_Contains()使用并具有相同的签名。这个插槽可以留给NULL,在这种情况下PySequence_Contains()简单地遍历序列,直到找到匹配为止
binaryfunc PySequenceMethods.sq_inplace_concat
该函数由PySequence_InPlaceConcat()使用,并具有相同的签名。它应该修改它的第一个操作数,然后返回它。这个插槽可以留给NULL,在这种情况下PySequence_InPlaceConcat()会回到PySequence_Concat()。它也用于增加的任务+=,在尝试数字inplace add之后nb_inplace_addslot.
ssizeargfunc PySequenceMethods.sq_inplace_repeat
功能由使用PySequence_InPlaceRepeat()并具有相同的标志。它应该修改它的第一个操作数,然后返回它。这个插槽可以留给NULL,在这种情况下PySequence_InPlaceRepeat()会回到PySequence_Repeat()。在使用*= slot.nb_inplace_multiply缓冲对象结构

 

进行数字内部乘法后,它也可用于增强的赋值

PyBufferProcs
这个结构保存了指向缓冲协议所需功能的指针。该协议定义了导出器对象如何将其内部数据暴露给消费者对象.
getbufferproc PyBufferProcs.bf_getbuffer
这个函数的签名是:

int (PyObject *exporter, Py_buffer *view, int flags);

exporter的规定处理view的请求填写flags。除了第(3)点,这个函数的实现必须采取以下步骤:

  1. 检查是否可以满足请求。如果没有,举起PyExc_BufferError,设置view->objNULL并返回-1.
  2. 填写要求的字段.
  3. 增加一个内部计数器输出的数量.
  4. 设置view->objexporter并增加view->obj.
  5. 返回0.

如果exporter是缓冲提供程序的链或树的一部分,可以使用两个主程序:

  • 重新导出:树的每个成员充当导出对象并将view->obj设置为对其自身的新引用.
  • 重定向:缓冲区请求被重定向到树的根对象。在这里,view->obj将成为对象的新参考.

view的各个字段在缓冲结构,出口商必须如何对特定要求作出反应的规则在缓冲请求类型.

指向的所有记忆Py_buffer结构属于导出器,必须保持有效,直到没有消费者为止.format, shape,strides, suboffsetsinternal对于消费者而言是只读的.

PyBuffer_FillInfo()提供了一种简单的方法来暴露简单的缓冲区,同时正确处理所有请求类型.

PyObject_GetBuffer()是消费者的界面,用于包含这个功能.

releasebufferproc PyBufferProcs.bf_releasebuffer
这个函数的签名是:

void (PyObject *exporter, Py_buffer *view);

处理释放缓冲区资源的请求。如果没有资源需要释放,PyBufferProcs.bf_releasebuffer可能是NULL。否则,此函数的标准实现将执行可选步骤:

  1. 减少内部计数器的导出次数.
  2. 如果计数器是0,则释放所有相关的内存使用view.

导出器必须使用internal字段来保存特定于缓冲区的资源。保证这个字段保持不变,而消费者可以传递原始缓冲区的副本作为view参数

这个函数不能递减view->obj,因为这是自动的在PyBuffer_Release()(这个方案可用于打破参考周期).

PyBuffer_Release()是包含此功能的消费者的接口.

&nbsp;

异步对象结构

3.5版本中的新功能

PyAsyncMethods
这个结构保存了指向实现等待异步迭代器对象所需的函数指针.

这是结构定义:

typedef struct {
    unaryfunc am_await;
    unaryfunc am_aiter;
    unaryfunc am_anext;
} PyAsyncMethods;
unaryfunc PyAsyncMethods.am_await
这个函数的签名是:

PyObject *am_await(PyObject *self)

返回的对象必须是一个迭代器,即PyIter_Check() mustreturn 1为它.

如果对象不是NULL,则此插槽可以设置为awaitable .

unaryfunc PyAsyncMethods.am_aiter
这个功能的签名是:

PyObject *am_aiter(PyObject *self)

必须退回awaitable 宾语。见__anext__()详情.

此插槽可设置为NULL如果一个对象没有实现异步迭代协议.

unaryfunc PyAsyncMethods.am_anext
这个函数的签名是:

PyObject *am_anext(PyObject *self)

必须返回等待 object。见__anext__()有关详细信息。此插槽可能设置为NULL.

评论被关闭。