On this page
Type Objects
Python 对象系统最重要的结构之一可能是定义新类型的结构:PyTypeObject结构。可以使用PyObject_*()
或PyType_*()
函数中的任何一种来处理类型对象,但是对于大多数 Python 应用程序而言,它们并没有提供很多有趣的东西。这些对象是对象行为方式的基础,因此它们对于解释器本身以及实现新类型的任何扩展模块都非常重要。
与大多数标准类型相比,类型对象相当大。其大小的原因是,每个类型对象都存储大量的值,主要是 C 函数指针,每个值都实现了类型Function的一小部分。本节将详细检查类型对象的字段。这些字段将按照它们在结构中出现的 Sequences 进行描述。
除了以下快速参考之外,Examples部分还概述了PyTypeObject的含义和用法。
Quick Reference
"tp slots"
如果定义了COUNT_ALLOCS
,则还存在以下(仅内部)字段:
-
- 括号中的插槽名称表明(有效)已弃用该插槽名称。尖括号中的名称应视为只读。方括号中的名称仅供内部使用。 “ ”(作为前缀)表示必填字段(必须为非
NULL
)。
- 括号中的插槽名称表明(有效)已弃用该插槽名称。尖括号中的名称应视为只读。方括号中的名称仅供内部使用。 “ ”(作为前缀)表示必填字段(必须为非
-
- Columns:
“ O” :设置为PyBaseObject_Type
“ T” :设置为PyType_Type
“ D” :默认值(如果广告位设置为NULL
)
X - PyType_Ready sets this value if it is NULL
~ - PyType_Ready always sets this value (it should be NULL)
? - PyType_Ready may set this value depending on other slots
Also see the inheritance column ("I").
“ I” :继承
X - type slot is inherited via PyType_Ready if defined with a NULL value
% - the slots of the sub-struct are inherited individually
G - inherited, but only in combination with other slots; see the slot's description
? - it's complicated; see the slot's description
请注意,某些插槽实际上是pass常规属性查找链继承的。
sub-slots
slot typedefs
有关更多详细信息,请参见下面的插槽类型 typedefs。
PyTypeObject Definition
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;
Py_ssize_t tp_vectorcall_offset;
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;
PyObject Slots
类型对象结构扩展了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时,在运行结束时打印仍然存在的对象。
- 这些字段仅在定义宏
Inheritance:
这些字段不被子类型继承。
- Py_ssize_t
PyObject.ob_refcnt
- 这是类型对象的引用计数,由
PyObject_HEAD_INIT
宏初始化为1
。请注意,对于静态分配的类型对象,类型的实例(ob_type
指向类型的对象)不作为引用。但是对于动态分配的类型对象,实例* do *被视为引用。
- 这是类型对象的引用计数,由
Inheritance:
子类型不继承此字段。
- 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
,如果是,则将其初始化为 Base Class 的ob_type
字段。 PyType_Ready()如果该字段非零,则不会更改。
Inheritance:
该字段由子类型继承。
PyVarObject Slots
- Py_ssize_t
PyVarObject.ob_size
- 对于静态分配的类型对象,应将其初始化为零。对于动态分配的类型对象,此字段具有特殊的内部含义。
Inheritance:
子类型不继承此字段。
PyTypeObject Slots
每个插槽都有一个描述继承的部分。如果在字段设置为NULL
时PyType_Ready()可以设置一个值,那么还将有一个“默认”部分。 (请注意,在PyBaseObject_Type
和PyType_Type上设置的许多字段实际上都充当默认值。)
- const char *
PyTypeObject.tp_name
- 指向包含类型名称的 NUL 终止的字符串的指针。对于可以作为模块全局变量访问的类型,字符串应为完整的模块名称,后跟一个圆点,然后是类型名称;对于内置类型,它应该只是类型名称。如果模块是软件包的子模块,则完整的软件包名称是完整模块名称的一部分。例如,在包
P
的子包Q
的模块M
中定义的名为T
的类型应具有tp_name初始值设定项"P.Q.M.T"
。
- 指向包含类型名称的 NUL 终止的字符串的指针。对于可以作为模块全局变量访问的类型,字符串应为完整的模块名称,后跟一个圆点,然后是类型名称;对于内置类型,它应该只是类型名称。如果模块是软件包的子模块,则完整的软件包名称是完整模块名称的一部分。例如,在包
对于动态分配的类型对象,它应该只是类型名称,而模块名称中明确存储的模块名称将作为键'__module__'
的值。
对于静态分配的类型对象,tp_name 字段应包含一个点。最后一个点之前的所有内容都可以作为__module__
属性访问,最后一个点之后的所有内容都可以作为name属性访问。
如果不存在点,则将整个tp_name字段作为name属性进行访问,并且未定义__module__
属性(除非如上所述在字典中进行了明确设置)。这意味着您的类型将无法腌制。此外,它不会在 pydoc 创建的模块文档中列出。
该字段不得为NULL
。它是PyTypeObject()中唯一的必填字段(可能是tp_itemsize除外)。
Inheritance:
子类型不继承此字段。
- 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_HEAD或PyObject_VAR_HEAD语句的实例中的字段(无论哪个用于语句实例结构),并且依次包含_ob_prev
和_ob_next
字段(如果存在)。这意味着获取tp_basicsize的初始化程序的唯一正确方法是在用于语句实例布局的结构上使用sizeof
运算符。基本大小不包括 GCHeaders 大小。
关于对齐的 Comments:如果变量项需要特定的对齐,则应passtp_basicsize的值来解决。示例:假设一个类型实现double
的数组。 tp_itemsize是sizeof(double)
。 tp_basicsize是sizeof(double)
的倍数是程序员的责任(假设这是double
的对齐要求)。
对于具有可变长度实例的任何类型,此字段不得为NULL
。
Inheritance:
这些字段由子类型分别继承。如果基本类型具有非零的tp_itemsize,则在子类型中将tp_itemsize设置为其他非零值通常是不安全的(尽管这取决于基本类型的实现)。
- destructor
PyTypeObject.tp_dealloc
- 指向实例析构函数的指针。除非该类型保证其实例永远不会被释放(如单例
None
和Ellipsis
),否则必须定义此函数。函数签名为:
- 指向实例析构函数的指针。除非该类型保证其实例永远不会被释放(如单例
void tp_dealloc(PyObject *self);
当新的引用计数为零时,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()。
最后,如果类型是堆分配的(Py_TPFLAGS_HEAPTYPE),则在调用类型解除分配器之后,解除分配器应减少其类型对象的引用计数。为了避免指针悬空,推荐的实现方法是:
static void foo_dealloc(foo_object *self) {
PyTypeObject *tp = Py_TYPE(self);
// free references and buffers here
tp->tp_free(self);
Py_DECREF(tp);
}
Inheritance:
该字段由子类型继承。
- Py_ssize_t
PyTypeObject.tp_vectorcall_offset
- 每个实例函数的可选偏移量,用于实现使用* vectorcall *协议调用对象,这是更简单的tp_call的更有效替代。
仅当设置了标志_Py_TPFLAGS_HAVE_VECTORCALL时,才使用此字段。如果是这样,则它必须是一个正整数,其中包含vectorcallfunc指针实例中的偏移量。签名与_PyObject_Vectorcall()相同:
PyObject *vectorcallfunc(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
- vectorcallfunc *指针可以为零,在这种情况下,该实例的行为就像未设置_Py_TPFLAGS_HAVE_VECTORCALL:调用该实例将回退到tp_call。
任何设置_Py_TPFLAGS_HAVE_VECTORCALL
的类也必须设置tp_call并确保其行为与* vectorcallfunc 函数一致。这可以pass将 tp_call *设置为PyVectorcall_Call
来完成:
- PyObject *
PyVectorcall_Call
(PyObject *callable ,PyObject tuple *,PyObject ** dict *)- 用 Tuples 和 dict 中分别给出的位置和关键字参数调用* callable 的 vectorcallfunc *。
该Function旨在在tp_call
插槽中使用。它不会退回到tp_call
,并且当前不检查_Py_TPFLAGS_HAVE_VECTORCALL
标志。要调用对象,请改用PyObject_Call函数之一。
Note
不建议heap types实现引导程序协议。当用户在 Python 代码中设置__call__
时,仅tp_call
被更新,可能使其与 vectorcall 函数不一致。
Note
tp_vectorcall_offset
插槽的语义是临时的,预计将在 Python 3.9 中完成。如果您使用 vectorcall,请计划为 Python 3.9 更新代码。
在版本 3.8 中更改:此插槽用于 Python 2.x 中的打印格式。在 Python 3.0 至 3.7 中,它已保留并命名为tp_print
。
Inheritance:
该字段由子类型与tp_call一起继承:当子类型的tp_call为NULL
时,子类型从其基本类型继承tp_vectorcall_offset。
请注意,heap types(包括 Python 中定义的子类)不继承_Py_TPFLAGS_HAVE_VECTORCALL标志。
- getattrfunc
PyTypeObject.tp_getattr
- 指向 get-attribute-string 函数的可选指针。
此字段已弃用。定义后,它应指向一个Function与tp_getattro函数相同的函数,但要使用 C 字符串而不是 Python 字符串对象来提供属性名称。
Inheritance:
群组:tp_getattr
,tp_getattro
该字段由子类型与tp_getattro一起继承:当子类型的tp_getattr和tp_getattro均为NULL
时,子类型从其基本类型继承tp_getattr和tp_getattro。
- setattrfunc
PyTypeObject.tp_setattr
- 指向用于设置和删除属性的函数的可选指针。
此字段已弃用。定义后,它应指向一个Function与tp_setattro函数相同的函数,但要使用 C 字符串而不是 Python 字符串对象来提供属性名称。
Inheritance:
群组:tp_setattr
,tp_setattro
该字段由子类型与tp_setattro一起继承:当子类型的tp_setattr和tp_setattro均为NULL
时,子类型从其基本类型继承tp_setattr和tp_setattro。
- PyAsyncMethods*
PyTypeObject.tp_as_async
- 指向一个附加结构的指针,该结构包含仅与在 C 级实现awaitable和asynchronous iterator协议的对象相关的字段。有关详细信息,请参见异步对象结构。
3.5 版的新Function:以前称为tp_compare
和tp_reserved
。
Inheritance:
tp_as_async字段不是继承的,但是包含的字段是分别继承的。
签名与PyObject_Repr()相同:
PyObject *tp_repr(PyObject *self);
该函数必须返回字符串或 Unicode 对象。理想情况下,此函数应该返回一个字符串,该字符串在给定合适的环境的情况下传递给eval()时,将返回具有相同值的对象。如果这不可行,则应返回以'<'
开头和'>'
结束的字符串,从中可以推导出对象的类型和值。
Inheritance:
该字段由子类型继承。
Default:
如果未设置此字段,则返回格式为<%s object at %p>
的字符串,其中%s
替换为类型名称,%p
替换为对象的内存地址。
- PyNumberMethods*
PyTypeObject.tp_as_number
- 指向包含仅与实现数字协议的对象相关的字段的附加结构的指针。这些字段记录在数字对象结构中。
Inheritance:
tp_as_number字段不是继承的,但是包含的字段是分别继承的。
- PySequenceMethods*
PyTypeObject.tp_as_sequence
- 指向包含仅与实现序列协议的对象相关的字段的附加结构的指针。这些字段记录在序列对象结构中。
Inheritance:
tp_as_sequence字段不是继承的,但是包含的字段是分别继承的。
- PyMappingMethods*
PyTypeObject.tp_as_mapping
- 指向包含仅与实现 Map 协议的对象相关的字段的附加结构的指针。这些字段记录在Map 对象结构中。
Inheritance:
tp_as_mapping字段不是继承的,但是包含的字段是分别继承的。
签名与PyObject_Hash()相同:
Py_hash_t tp_hash(PyObject *);
值-1
不应作为常规返回值返回;当在哈希值的计算过程中发生错误时,该函数应设置一个异常并返回-1
。
如果未设置此字段(未设置和 tp_richcompare
),则try获取对象的哈希值将引发TypeError。这与将其设置为PyObject_HashNotImplemented()相同。
可以将该字段显式设置为PyObject_HashNotImplemented(),以阻止哈希方法从父类型继承。在 Python 级别上,这被解释为等效于__hash__ = None
,从而导致isinstance(o, collections.Hashable)
正确返回False
。请注意,反之亦然-在 Python 级别的类上设置__hash__ = None
将导致tp_hash
插槽设置为PyObject_HashNotImplemented()。
Inheritance:
群组:tp_hash
,tp_richcompare
该字段由子类型与tp_richcompare一起继承:当子类型的tp_richcompare和tp_hash均为NULL
时,子类型既继承tp_richcompare又tp_hash。
- ternaryfunc
PyTypeObject.tp_call
- 指向实现调用对象的函数的可选指针。如果对象不可调用,则应为
NULL
。签名与PyObject_Call()相同:
- 指向实现调用对象的函数的可选指针。如果对象不可调用,则应为
PyObject *tp_call(PyObject *self, PyObject *args, PyObject *kwargs);
Inheritance:
该字段由子类型继承。
- reprfunc
PyTypeObject.tp_str
- 指向实现内置操作str()的函数的可选指针。 (请注意,str现在是一种类型,str()调用该类型的构造函数。此构造函数调用PyObject_Str()进行实际工作,而PyObject_Str()将调用此处理程序。)
签名与PyObject_Str()相同:
PyObject *tp_str(PyObject *self);
该函数必须返回字符串或 Unicode 对象。它应该是对象的“友好”字符串表示形式,因为这是print()函数将使用的表示形式。
Inheritance:
该字段由子类型继承。
Default:
如果未设置此字段,则调用PyObject_Repr()以返回字符串表示形式。
- getattrofunc
PyTypeObject.tp_getattro
- 指向 get-attribute 函数的可选指针。
签名与PyObject_GetAttr()相同:
PyObject *tp_getattro(PyObject *self, PyObject *attr);
通常将此字段设置为PyObject_GenericGetAttr()很方便,这实现了查找对象属性的常规方法。
Inheritance:
群组:tp_getattr
,tp_getattro
该字段由子类型与tp_getattr一起继承:当子类型的tp_getattr和tp_getattro均为NULL
时,子类型从其基本类型继承tp_getattr和tp_getattro。
Default:
PyBaseObject_Type
使用PyObject_GenericGetAttr()。
- setattrofunc
PyTypeObject.tp_setattro
- 指向用于设置和删除属性的函数的可选指针。
签名与PyObject_SetAttr()相同:
PyObject *tp_setattro(PyObject *self, PyObject *attr, PyObject *value);
此外,必须支持将* value *设置为NULL
以删除属性。通常将此字段设置为PyObject_GenericSetAttr()是很方便的,这实现了设置对象属性的常规方法。
Inheritance:
群组:tp_setattr
,tp_setattro
该字段由子类型与tp_setattr一起继承:当子类型的tp_setattr和tp_setattro均为NULL
时,子类型从其基本类型继承tp_setattr和tp_setattro。
Default:
PyBaseObject_Type
使用PyObject_GenericSetAttr()。
- PyBufferProcs*
PyTypeObject.tp_as_buffer
- 指向包含仅与实现缓冲区接口的对象相关的字段的附加结构的指针。这些字段记录在缓冲区对象结构中。
Inheritance:
tp_as_buffer字段不是继承的,但是包含的字段是分别继承的。
- 未签名的长
PyTypeObject.tp_flags
- 该字段是各种标志的位掩码。一些标志指示在某些情况下的变体语义。其他用于指示类型对象(或在passtp_as_number,tp_as_sequence,tp_as_mapping和tp_as_buffer引用的扩展结构中)的某些历史上并不总是有效的字段;如果清除了此类标志位,则必须禁止访问其保护的类型字段,而应将其视为零或
NULL
值。
- 该字段是各种标志的位掩码。一些标志指示在某些情况下的变体语义。其他用于指示类型对象(或在passtp_as_number,tp_as_sequence,tp_as_mapping和tp_as_buffer引用的扩展结构中)的某些历史上并不总是有效的字段;如果清除了此类标志位,则必须禁止访问其保护的类型字段,而应将其视为零或
Inheritance:
该字段的继承很复杂。大多数标志位是单独继承的,即,如果基本类型设置了标志位,则子类型将继承此标志位。如果继承了扩展结构,则严格继承与扩展结构有关的标志位,即标志位的基本类型值与指向扩展结构的指针一起复制到子类型中。 Py_TPFLAGS_HAVE_GC标志位与tp_traverse和tp_clear字段一起继承,即,如果子类型中的Py_TPFLAGS_HAVE_GC标志位清零并且子类型中的tp_traverse和tp_clear字段存在并且具有NULL
值。
Default:
PyBaseObject_Type
使用Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
。
Bit Masks:
当前定义了以下位掩码;可以使用|
运算符对它们进行“或”运算以形成tp_flags字段的值。宏PyType_HasFeature()接受类型和标志值* tp 和 f *,并检查tp->tp_flags & f
是否为非零。
Py_TPFLAGS_HEAPTYPE
- 当类型对象本身分配在堆上时(例如,使用PyType_FromSpec()动态创建的类型),将设置此位。在这种情况下,其实例的
ob_type
字段被视为对类型的引用,并且在创建新实例时将类型对象添加为 INCREF,而在实例被销毁时则为 DECREF(这不适用于以下类型的实例:子类型;仅实例的 ob_type 引用的类型被 INCREF(或 DECREF)。
- 当类型对象本身分配在堆上时(例如,使用PyType_FromSpec()动态创建的类型),将设置此位。在这种情况下,其实例的
Inheritance:
???
Py_TPFLAGS_BASETYPE
- 当该类型可用作另一种类型的基本类型时,将设置此位。如果清除此位,则该类型不能被子类型化(类似于 Java 中的“finally”类)。
Inheritance:
???
Py_TPFLAGS_READY
- 当类型对象已由PyType_Ready()完全初始化时,将设置此位。
Inheritance:
???
Py_TPFLAGS_READYING
- 当PyType_Ready()正在初始化类型对象的过程中,该位置 1.
Inheritance:
???
Py_TPFLAGS_HAVE_GC
- 当对象支持垃圾回收时,将设置此位。如果设置了此位,则必须使用PyObject_GC_New()创建实例,并使用PyObject_GC_Del()销毁实例。 支持循环垃圾收集部分中的更多信息。该位还暗示在类型对象中存在与 GC 相关的字段tp_traverse和tp_clear。
Inheritance:
群组:Py_TPFLAGS_HAVE_GC,tp_traverse
,tp_clear
Py_TPFLAGS_HAVE_GC标志位与tp_traverse
和tp_clear
字段一起继承,即,如果子类型中的Py_TPFLAGS_HAVE_GC标志位清零并且子类型中的tp_traverse
和tp_clear
字段存在并且具有NULL
值。
Py_TPFLAGS_DEFAULT
- 这是与类型对象及其扩展结构中某些字段的存在有关的所有位的位掩码。当前,它包括以下位:
Py_TPFLAGS_HAVE_STACKLESS_EXTENSION
,Py_TPFLAGS_HAVE_VERSION_TAG
。
- 这是与类型对象及其扩展结构中某些字段的存在有关的所有位的位掩码。当前,它包括以下位:
Inheritance:
???
Py_TPFLAGS_METHOD_DESCRIPTOR
- 该位指示对象的行为类似于未绑定方法。
如果此标志设置为type(meth)
,则:
meth.__get__(obj, cls)(*args, **kwds)
(其中obj
不是 None)必须等于meth(obj, *args, **kwds)
。meth.__get__(None, cls)(*args, **kwds)
必须等于meth(*args, **kwds)
。
此标志启用了对诸如obj.meth()
之类的典型方法调用的优化:避免为obj.meth
创建临时的“绑定方法”对象。
3.8 版的新Function。
Inheritance:
堆类型永远不会继承此标志。对于 extensions 类型,只要继承tp_descr_get就会继承它。
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 版的新Function。
从 3.8 版开始不推荐使用:不再需要此标志,因为解释器假定tp_finalize插槽始终存在于类型结构中。
_Py_TPFLAGS_HAVE_VECTORCALL
- 当该类实现矢量调用协议时,将设置此位。有关详情,请参见tp_vectorcall_offset。
Inheritance:
如果tp_flags
不被覆盖,则在* static *子类型上设置此位:当子类型的tp_call为NULL
且未设置子类型的Py_TPFLAGS_HEAPTYPE
时,子类型从其基本类型继承_Py_TPFLAGS_HAVE_VECTORCALL
。
Heap types不继承_Py_TPFLAGS_HAVE_VECTORCALL
。
Note
该标志是临时的,有望在 Python 3.9 中公开,其名称不同,并且语义可能有所更改。如果您使用 vectorcall,请计划为 Python 3.9 更新代码。
3.8 版的新Function。
- const char *
PyTypeObject.tp_doc
- 指向 NUL 终止的 C 字符串的可选指针,为该类型对象提供 docstring。这在类型和类型的实例上显示为
__doc__
属性。
- 指向 NUL 终止的 C 字符串的可选指针,为该类型对象提供 docstring。这在类型和类型的实例上显示为
Inheritance:
该字段不是子类型继承的。
- traverseproc
PyTypeObject.tp_traverse
- 指向垃圾收集器遍历函数的可选指针。仅在设置了Py_TPFLAGS_HAVE_GC标志位时使用。签名是:
int tp_traverse(PyObject *self, visitproc visit, void *arg);
有关 Python 的垃圾回收方案的更多信息,请参见支持循环垃圾收集部分。
垃圾回收器使用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()函数将其包括在内。
Warning
在实现tp_traverse时,仅必须访问实例拥有的成员(pass强烈引用它们)。例如,如果某个对象passtp_weaklist插槽支持弱引用,则必须 不要 访问支持链接列表(* tp_weaklist *指向)的指针,因为该实例并不直接拥有对其自身的弱引用(这里有弱引用列表来支持弱引用机制,但是实例对其中的元素没有强引用,因为即使实例仍然存在,也可以删除它们。
请注意,Py_VISIT()要求local_traverse()
的* visit 和 arg *参数具有这些特定名称;不要给他们起任何名字。
Inheritance:
群组:Py_TPFLAGS_HAVE_GC,tp_traverse
,tp_clear
该字段由子类型以及tp_clear和Py_TPFLAGS_HAVE_GC标志位继承:如果标志位tp_traverse和tp_clear在子类型中都为零,则它们都从基本类型继承。
- inquiry
PyTypeObject.tp_clear
- 指向垃圾回收器的清除函数的可选指针。仅在设置了Py_TPFLAGS_HAVE_GC标志位时使用。签名是:
int tp_clear(PyObject *);
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 的垃圾回收方案的更多信息,请参见支持循环垃圾收集部分。
Inheritance:
群组:Py_TPFLAGS_HAVE_GC,tp_traverse
,tp_clear
该字段由子类型以及tp_traverse和Py_TPFLAGS_HAVE_GC标志位继承:如果标志位tp_traverse和tp_clear在子类型中都为零,则它们都从基本类型继承。
- richcmpfunc
PyTypeObject.tp_richcompare
- 指向丰富比较Function的可选指针,其签名为:
PyObject *tp_richcompare(PyObject *self, PyObject *other, int op);
确保第一个参数是PyTypeObject定义的类型的实例。
该函数应返回比较结果(通常为Py_True
或Py_False
)。如果比较未定义,则必须返回Py_NotImplemented
,如果发生另一个错误,则必须返回NULL
并设置异常条件。
以下常量被定义为tp_richcompare和PyObject_RichCompare()的第三个参数:
Constant | Comparison |
---|---|
Py_LT |
< |
Py_LE |
<= |
Py_EQ |
== |
Py_NE |
!= |
Py_GT |
> |
Py_GE |
>= |
定义以下宏以简化编写丰富的比较Function的过程:
- PyObject *
Py_RETURN_RICHCOMPARE
(VAL_A,VAL_B,int * op *)- 从函数返回
Py_True
或Py_False
,具体取决于比较结果。 VAL_A 和 VAL_B 必须可由 C 比较运算符排序(例如,它们可以是 C int 或 float)。第三个参数指定所请求的操作,如PyObject_RichCompare()。
- 从函数返回
返回值的参考计数正确增加。
出错时,设置一个异常并从该函数返回NULL
。
3.7 版中的新Function。
Inheritance:
群组:tp_hash
,tp_richcompare
该字段由子类型与tp_hash一起继承:当子类型的tp_richcompare和tp_hash均为NULL
时,子类型继承tp_richcompare和tp_hash。
Default:
PyBaseObject_Type
提供了tp_richcompare
实现,可以继承。但是,如果仅定义tp_hash
,则不会使用继承的函数,并且该类型的实例将无法参与任何比较。
- Py_ssize_t
PyTypeObject.tp_weaklistoffset
- 如果此类型的实例是弱引用的,则此字段大于零,并且包含弱引用列表头的实例结构中的偏移量(忽略 GC 头,如果存在);该偏移量由
PyObject_ClearWeakRefs()
和PyWeakref_*()
函数使用。实例结构需要包含类型为PyObject*的字段,该字段已初始化为NULL
。
- 如果此类型的实例是弱引用的,则此字段大于零,并且包含弱引用列表头的实例结构中的偏移量(忽略 GC 头,如果存在);该偏移量由
不要将此字段与tp_weaklist混淆;这是对类型对象本身的弱引用的列表头。
Inheritance:
该字段由子类型继承,但请参见下面列出的规则。子类型可以覆盖此偏移量。这意味着子类型使用与基本类型不同的弱引用列表头。由于列表头始终是passtp_weaklistoffset找到的,因此这不是问题。
当由类语句定义的类型没有slots语句,并且其基本类型均不可弱引用时,可pass在实例布局中添加弱引用列表头插槽并设置该插槽的偏移量tp_weaklistoffset来使该类型成为弱引用。
当类型的__slots__
语句包含名为__weakref__
的插槽时,该插槽将成为该类型实例的弱引用列表头,并且该插槽的偏移量将存储在该类型的tp_weaklistoffset中。
当类型的__slots__
语句不包含名为__weakref__
的插槽时,该类型将从其基本类型继承其tp_weaklistoffset。
- getiterfunc
PyTypeObject.tp_iter
- 指向函数的可选指针,该函数返回对象的迭代器。它的存在通常表示此类型的实例是可迭代的(尽管如果没有此Function,序列可能是可迭代的)。
该函数具有与PyObject_GetIter()相同的签名:
PyObject *tp_iter(PyObject *self);
Inheritance:
该字段由子类型继承。
- iternextfunc
PyTypeObject.tp_iternext
- 指向函数的可选指针,该函数返回迭代器中的下一项。签名是:
PyObject *tp_iternext(PyObject *self);
当迭代器用尽时,它必须返回NULL
;可以设置StopIterationexception,也可以不设置。当发生另一个错误时,它也必须返回NULL
。它的存在表明该类型的实例是迭代器。
迭代器类型还应该定义tp_iter函数,并且该函数应返回迭代器实例本身(而不是新的迭代器实例)。
此Function具有与PyIter_Next()相同的签名。
Inheritance:
该字段由子类型继承。
- 结构PyMethodDef *
PyTypeObject.tp_methods
- 指向由PyMethodDef结构终止的静态
NULL
终止数组的可选指针,语句了这种类型的常规方法。
- 指向由PyMethodDef结构终止的静态
对于数组中的每个条目,将一个条目添加到包含方法 Descriptors 的类型的字典中(请参见下面的tp_dict)。
Inheritance:
子类型不会继承该字段(方法是pass其他机制继承的)。
- 结构PyMemberDef *
PyTypeObject.tp_members
- 指向由PyMemberDef结构终止的静态
NULL
终止数组的可选指针,语句此类型实例的常规数据成员(字段或插槽)。
- 指向由PyMemberDef结构终止的静态
对于数组中的每个条目,将一个条目添加到包含成员 Descriptors 的类型的字典中(请参见下面的tp_dict)。
Inheritance:
子类型不继承该字段(成员pass不同的机制继承)。
- 结构PyGetSetDef *
PyTypeObject.tp_getset
- 指向由PyGetSetDef结构终止的静态
NULL
终止数组的可选指针,语句此类型实例的计算属性。
- 指向由PyGetSetDef结构终止的静态
对于数组中的每个条目,将一个条目添加到包含 getsetDescriptors 的类型的字典中(请参见下面的tp_dict)。
Inheritance:
子类型不会继承此字段(计算的属性是pass其他机制继承的)。
- PyTypeObject*
PyTypeObject.tp_base
- 指向从其继承类型属性的基本类型的可选指针。在此级别上,仅支持单继承。多重继承要求pass调用元类型来动态创建类型对象。
Note
插槽初始化受初始化全局规则的约束。 C99 要求初始化程序为“地址常量”。诸如PyType_GenericNew()之类的函数指示符(会隐式转换为指针)是有效的 C99 地址常量。
但是,不需要将一元'&'运算符应用于诸如PyBaseObject_Type()
之类的非静态变量即可生成地址常数。编译器可能支持(gcc 支持),MSVC 不支持。两种编译器在此特定行为上均严格遵循标准。
因此,应在扩展模块的 init 函数中设置tp_base。
Inheritance:
子类型(显然)不继承此字段。
Default:
该字段默认为&PyBaseObject_Type
(对于 Python 程序员而言,称为object类型)。
- PyObject*
PyTypeObject.tp_dict
- 类型的字典存储在PyType_Ready()处。
在调用 PyType_Ready 之前,通常应将该字段初始化为NULL
。它也可以初始化为包含该类型的初始属性的字典。 PyType_Ready()初始化类型后,只有该类型的额外属性不对应于重载的操作(例如add()),才可以将其添加到此字典中。
Inheritance:
子类型不会继承该字段(尽管此处定义的属性是pass其他机制继承的)。
Default:
如果此字段为NULL
,则PyType_Ready()将为其分配一个新字典。
Warning
在字典 C-API 上使用PyDict_SetItem()或以其他方式修改tp_dict是不安全的。
- descrgetfunc
PyTypeObject.tp_descr_get
- 指向“Descriptors 获取”Function的可选指针。
函数签名为:
PyObject * tp_descr_get(PyObject *self, PyObject *obj, PyObject *type);
Inheritance:
该字段由子类型继承。
- descrsetfunc
PyTypeObject.tp_descr_set
- 指向用于设置和删除 Descriptors 值的函数的可选指针。
函数签名为:
int tp_descr_set(PyObject *self, PyObject *obj, PyObject *value);
- value *参数设置为
NULL
以删除该值。
Inheritance:
该字段由子类型继承。
- Py_ssize_t
PyTypeObject.tp_dictoffset
- 如果此类型的实例具有包含实例变量的字典,则此字段为非零值,并且包含实例变量字典类型的实例中的偏移量;否则为 0. PyObject_GenericGetAttr()使用此偏移量。
不要将此字段与tp_dict混淆;那是类型对象本身的属性的字典。
如果此字段的值大于零,则它指定从实例结构开始的偏移量。如果该值小于零,则指定与实例结构的* end *的偏移量。负偏移量使用起来更昂贵,并且仅当实例结构包含可变长度部分时才应使用。例如,这用于将实例变量字典添加到str或tuple的子类型。注意,在这种情况下,即使基本对象布局中不包含字典,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_itemsize和tp_dictoffset是从类型对象中获取的,而ob_size
是从实例中获取的。采用绝对值是因为 int 使用ob_size
的符号来存储数字的符号。 (永远不需要自己进行此计算;它由_PyObject_GetDictPtr()
为您完成.)
Inheritance:
该字段由子类型继承,但请参见下面列出的规则。子类型可以覆盖此偏移量。这意味着子类型实例以与基本类型不同的偏移量存储字典。由于字典始终是passtp_dictoffset找到的,因此这不是问题。
如果由类语句定义的类型没有slots语句,并且其基本类型都没有实例变量字典,则将字典槽添加到实例布局,并将tp_dictoffset设置为该槽的偏移量。
当由类语句定义的类型具有__slots__
语句时,该类型将从其基本类型继承其tp_dictoffset。
(在__slots__
语句中添加名为dict的插槽并没有达到预期的效果,只会引起混淆。也许应该像__weakref__
一样将此Function添加为Function。)
Default:
该插槽没有默认设置。对于静态类型,如果该字段为NULL
,则不会为实例创建__dict__
。
- initproc
PyTypeObject.tp_init
- 实例初始化函数的可选指针。
此函数对应于init()类的方法。像init()一样,可以在不调用init()的情况下创建实例,并且可以pass再次调用其init()方法来重新初始化实例。
函数签名为:
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。
成功返回0
,返回-1
并设置错误异常。
Inheritance:
该字段由子类型继承。
Default:
对于静态类型,此字段没有默认值。
- allocfunc
PyTypeObject.tp_alloc
- 指向实例分配Function的可选指针。
函数签名为:
PyObject *tp_alloc(PyTypeObject *self, Py_ssize_t nitems);
Inheritance:
该字段是由静态子类型继承的,而不是由动态子类型(由类语句创建的子类型)继承的。
Default:
对于动态子类型,此字段始终设置为PyType_GenericAlloc(),以强制执行标准堆分配策略。
对于静态子类型,PyBaseObject_Type
使用PyType_GenericAlloc()。这是所有静态定义类型的建议值。
- newfunc
PyTypeObject.tp_new
- 指向实例创建函数的可选指针。
函数签名为:
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进行。
Inheritance:
此字段由子类型继承,除了它不是由tp_base是NULL
或&PyBaseObject_Type
的静态类型继承。
Default:
对于静态类型,此字段没有默认值。这意味着,如果插槽定义为NULL
,则不能调用该类型来创建新实例;大概还有其他创建实例的方法,例如工厂函数。
- freefunc
PyTypeObject.tp_free
- 实例释放函数的可选指针。它的签名是:
void tp_free(void *self);
与此签名兼容的初始化器是PyObject_Free()。
Inheritance:
该字段是由静态子类型继承的,而不是由动态子类型(由类语句创建的子类型)继承的
Default:
在动态子类型中,此字段设置为适合与PyType_GenericAlloc()和Py_TPFLAGS_HAVE_GC标志位的值匹配的解除分配器。
对于静态子类型,PyBaseObject_Type
使用 PyObject_Del。
- inquiry
PyTypeObject.tp_is_gc
- 指向垃圾收集器调用的函数的可选指针。
垃圾收集器需要知道特定对象是否可收集。通常,查看对象类型的tp_flags字段并检查Py_TPFLAGS_HAVE_GC标志位就足够了。但是某些类型混合了静态和动态分配的实例,并且静态分配的实例是不可收集的。这些类型应定义此Function。对于可收集的实例,它应返回1
,对于不可收集的实例应返回0
。签名是:
int tp_is_gc(PyObject *self);
(这种情况的唯一示例就是类型本身.元类型PyType_Type定义了此函数,以区分静态分配类型和动态分配类型。)
Inheritance:
该字段由子类型继承。
Default:
该插槽没有默认设置。如果此字段为NULL
,则Py_TPFLAGS_HAVE_GC用作Function等效项。
- PyObject*
PyTypeObject.tp_bases
- 基本类型的 Tuples。
这是为由类语句创建的类型设置的。对于静态定义的类型,它应该为NULL
。
Inheritance:
此字段未继承。
Inheritance:
此字段未继承;它由PyType_Ready()重新计算。
- PyObject*
PyTypeObject.tp_cache
- 没用过。仅供内部使用。
Inheritance:
此字段未继承。
- PyObject*
PyTypeObject.tp_subclasses
- 对子类的弱引用列表。仅供内部使用。
Inheritance:
此字段未继承。
- PyObject*
PyTypeObject.tp_weaklist
- 弱引用列表头,用于对此类型对象的弱引用。不继承。仅供内部使用。
Inheritance:
此字段未继承。
destructor
PyTypeObject.tp_del
- 此字段已弃用。请改用tp_finalize。
unsigned int
PyTypeObject.tp_version_tag
- 用于索引方法缓存。仅供内部使用。
Inheritance:
此字段未继承。
- destructor
PyTypeObject.tp_finalize
- 实例完成函数的可选指针。它的签名是:
void tp_finalize(PyObject *self);
如果设置了tp_finalize,则解释器在确定实例时调用一次。从垃圾回收器(如果实例是一个隔离的引用周期的一部分)中调用它,或者在对象被释放之前。无论哪种方式,都可以确保在trybreak参考周期之前调用它,以确保它找到处于健全状态的对象。
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);
}
为了使该字段得到考虑(甚至pass继承),还必须设置Py_TPFLAGS_HAVE_FINALIZE标志位。
Inheritance:
该字段由子类型继承。
3.4 版的新Function。
See also
“安全对象完成”( PEP 442)
其余字段仅在定义了Function测试宏COUNT_ALLOCS
的情况下定义,并且仅供内部使用。为了完整起见,此处记录了它们。这些字段都不是子类型继承的。
Py_ssize_t
PyTypeObject.tp_allocs
- 分配数。
Py_ssize_t
PyTypeObject.tp_frees
- 免费数量。
Py_ssize_t
PyTypeObject.tp_maxalloc
- 同时分配的最大对象数。
PyTypeObject*
PyTypeObject.tp_prev
- 指向带有非零tp_allocs字段的上一个类型对象的指针。
PyTypeObject*
PyTypeObject.tp_next
- 指向带有非零tp_allocs字段的下一个类型对象的指针。
另外,请注意,在垃圾回收的 Python 中,可以从任何 Python 线程调用tp_dealloc,而不仅仅是创建对象的线程(如果该对象成为 refcount 周期的一部分,则该周期可以由任何对象上的垃圾回收来收集线)。对于 Python API 调用而言,这不是问题,因为在其上调用 tp_dealloc 的线程将拥有全局解释器锁(GIL)。但是,如果要销毁的对象又销毁了其他 C 或 C 库中的对象,则应注意确保销毁线程上名为 tp_dealloc 的那些对象不会违反该库的任何假设。
Heap Types
传统上,用 C 代码定义的类型是* static *,也就是说,静态PyTypeObject结构直接在代码中定义并使用PyType_Ready()初始化。
这导致相对于 Python 中定义的类型而言,类型受到限制:
静态类型仅限于一种基础,即它们不能使用多重继承。
静态类型对象(但不一定是它们的实例)是不可变的。无法从 Python 添加或修改类型对象的属性。
静态类型对象在sub-interpreters之间共享,因此它们不应包含任何特定于子解释器的状态。
另外,由于PyTypeObject不是stable ABI的一部分,因此必须为特定的 Python 次要版本编译所有使用静态类型的扩展模块。
静态类型的替代方法是* heap-allocated types 或简称为 heap types *,它们与 Python 的class
语句创建的类紧密对应。
这是pass填充PyType_Spec结构并调用PyType_FromSpecWithBases()来完成的。
数字对象结构
PyNumberMethods
- 此结构包含指向对象用来实现数字协议的Function的指针。 Number Protocol部分中记录的名称相似的Function使用每个Function。
这是结构定义:
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;
Note
二元和三元函数必须检查其所有操作数的类型,并执行必要的转换(至少一个操作数是已定义类型的实例)。如果未为给定的操作数定义操作,则二进制和三进制函数必须返回Py_NotImplemented
,如果发生另一个错误,则它们必须返回NULL
并设置异常。
Note
nb_reserved
字段应始终为NULL
。它以前称为nb_long
,并在 Python 3.0.1 中重命名。
binaryfunc
PyNumberMethods.nb_add
binaryfunc
PyNumberMethods.nb_subtract
binaryfunc
PyNumberMethods.nb_multiply
binaryfunc
PyNumberMethods.nb_remainder
binaryfunc
PyNumberMethods.nb_divmod
ternaryfunc
PyNumberMethods.nb_power
unaryfunc
PyNumberMethods.nb_negative
unaryfunc
PyNumberMethods.nb_positive
unaryfunc
PyNumberMethods.nb_absolute
inquiry
PyNumberMethods.nb_bool
unaryfunc
PyNumberMethods.nb_invert
binaryfunc
PyNumberMethods.nb_lshift
binaryfunc
PyNumberMethods.nb_rshift
binaryfunc
PyNumberMethods.nb_and
binaryfunc
PyNumberMethods.nb_xor
binaryfunc
PyNumberMethods.nb_or
unaryfunc
PyNumberMethods.nb_int
无效*
PyNumberMethods.nb_reserved
unaryfunc
PyNumberMethods.nb_float
binaryfunc
PyNumberMethods.nb_inplace_add
binaryfunc
PyNumberMethods.nb_inplace_subtract
binaryfunc
PyNumberMethods.nb_inplace_multiply
binaryfunc
PyNumberMethods.nb_inplace_remainder
ternaryfunc
PyNumberMethods.nb_inplace_power
binaryfunc
PyNumberMethods.nb_inplace_lshift
binaryfunc
PyNumberMethods.nb_inplace_rshift
binaryfunc
PyNumberMethods.nb_inplace_and
binaryfunc
PyNumberMethods.nb_inplace_xor
binaryfunc
PyNumberMethods.nb_inplace_or
binaryfunc
PyNumberMethods.nb_floor_divide
binaryfunc
PyNumberMethods.nb_true_divide
binaryfunc
PyNumberMethods.nb_inplace_floor_divide
binaryfunc
PyNumberMethods.nb_inplace_true_divide
unaryfunc
PyNumberMethods.nb_index
binaryfunc
PyNumberMethods.nb_matrix_multiply
binaryfunc
PyNumberMethods.nb_inplace_matrix_multiply
Map 对象结构
PyMappingMethods
- 此结构保存指向对象用来实现 Map 协议的Function的指针。它具有三个成员:
lenfunc
PyMappingMethods.mp_length
- PyMapping_Size()和PyObject_Size()使用此Function,并且具有相同的签名。如果对象没有定义的长度,则可以将此插槽设置为
NULL
。
- PyMapping_Size()和PyObject_Size()使用此Function,并且具有相同的签名。如果对象没有定义的长度,则可以将此插槽设置为
binaryfunc
PyMappingMethods.mp_subscript
- PyObject_GetItem()和PySequence_GetSlice()使用此函数,并且具有与
PyObject_GetItem()
相同的签名。必须填充此插槽,PyMapping_Check()函数才能返回1
,否则可以为NULL
。
- PyObject_GetItem()和PySequence_GetSlice()使用此函数,并且具有与
objobjargproc
PyMappingMethods.mp_ass_subscript
- PyObject_SetItem(),PyObject_DelItem(),
PyObject_SetSlice()
和PyObject_DelSlice()
使用此Function。它具有与PyObject_SetItem()
相同的签名,但是* v *也可以设置为NULL
以删除项目。如果此插槽是NULL
,则对象不支持项目分配和删除。
- PyObject_SetItem(),PyObject_DelItem(),
序列对象结构
PySequenceMethods
- 此结构保存指向对象用来实现序列协议的Function的指针。
lenfunc
PySequenceMethods.sq_length
- PySequence_Size()和PyObject_Size()使用此Function,并且具有相同的签名。它也用于passsq_item和sq_ass_item插槽处理负索引。
binaryfunc
PySequenceMethods.sq_concat
- PySequence_Concat()使用此Function,并且具有相同的签名。trypassnb_add插槽进行数字加法后,
+
运算符也使用它。
- PySequence_Concat()使用此Function,并且具有相同的签名。trypassnb_add插槽进行数字加法后,
ssizeargfunc
PySequenceMethods.sq_repeat
- PySequence_Repeat()使用此Function,并且具有相同的签名。trypassnb_multiply插槽进行数值乘法后,
*
运算符也使用它。
- PySequence_Repeat()使用此Function,并且具有相同的签名。trypassnb_multiply插槽进行数值乘法后,
ssizeargfunc
PySequenceMethods.sq_item
- PySequence_GetItem()使用此Function,并且具有相同的签名。passmp_subscript插槽try订阅后,PyObject_GetItem()也会使用它。必须填充此插槽,PySequence_Check()函数才能返回
1
,否则可以为NULL
。
- PySequence_GetItem()使用此Function,并且具有相同的签名。passmp_subscript插槽try订阅后,PyObject_GetItem()也会使用它。必须填充此插槽,PySequence_Check()函数才能返回
负索引的处理方式如下:如果填充了sq_length
插槽,则会调用它,并且使用序列长度来计算传递给sq_item
的正索引。如果sq_length
是NULL
,则将索引原样传递给函数。
ssizeobjargproc
PySequenceMethods.sq_ass_item
- PySequence_SetItem()使用此Function,并且具有相同的签名。在trypassmp_ass_subscript插槽分配和删除项目后,PyObject_SetItem()和PyObject_DelItem()也使用它。如果对象不支持项目分配和删除,则该插槽可以留为
NULL
。
- PySequence_SetItem()使用此Function,并且具有相同的签名。在trypassmp_ass_subscript插槽分配和删除项目后,PyObject_SetItem()和PyObject_DelItem()也使用它。如果对象不支持项目分配和删除,则该插槽可以留为
objobjproc
PySequenceMethods.sq_contains
- PySequence_Contains()可以使用此Function,并且具有相同的签名。该时隙可以留给
NULL
,在这种情况下PySequence_Contains()
会简单地遍历序列,直到找到匹配项为止。
- PySequence_Contains()可以使用此Function,并且具有相同的签名。该时隙可以留给
binaryfunc
PySequenceMethods.sq_inplace_concat
- PySequence_InPlaceConcat()使用此Function,并且具有相同的签名。它应该修改其第一个操作数,然后将其返回。该插槽可以留给
NULL
,在这种情况下PySequence_InPlaceConcat()
将退回到PySequence_Concat()。passnb_inplace_add插槽try就地数字加法后,扩展分配+=
也使用它。
- PySequence_InPlaceConcat()使用此Function,并且具有相同的签名。它应该修改其第一个操作数,然后将其返回。该插槽可以留给
ssizeargfunc
PySequenceMethods.sq_inplace_repeat
- PySequence_InPlaceRepeat()使用此Function,并且具有相同的签名。它应该修改其第一个操作数,然后将其返回。该插槽可以留给
NULL
,在这种情况下PySequence_InPlaceRepeat()
将退回到PySequence_Repeat()。passnb_inplace_multiply插槽try数字就地乘法后,扩展分配*=
也使用它。
- PySequence_InPlaceRepeat()使用此Function,并且具有相同的签名。它应该修改其第一个操作数,然后将其返回。该插槽可以留给
缓冲区对象结构
PyBufferProcs
- 该结构包含指向Buffer protocol所需Function的指针。该协议定义了导出器对象如何将其内部数据公开给使用者对象。
getbufferproc
PyBufferProcs.bf_getbuffer
- 该函数的签名是:
int (PyObject *exporter, Py_buffer *view, int flags);
处理对* exporter 的请求,以填写 flags 指定的* view *。除第(3)点外,此Function的实现必须采取以下步骤:
检查请求是否可以满足。如果不是,请加注
PyExc_BufferError
,将view->obj
设置为NULL
并返回-1
。填写要求的字段。
为 Export 数量增加一个内部计数器。
将
view->obj
设置为* exporter *并增加view->obj
。返回
0
。
如果* exporter *是缓冲区提供程序的链或树的一部分,则可以使用两个主要方案:
重新导出:树的每个成员都充当导出对象,并将
view->obj
设置为对其自身的新引用。重定向:缓冲区请求被重定向到树的根对象。在此,
view->obj
是对根对象的新引用。
Buffer structure部分描述了* view *的各个字段,而缓冲区请求类型部分则描述了 Export 商如何对特定请求做出反应的规则。
Py_buffer结构中指向的所有内存都属于导出程序,并且必须保持有效,直到没有任何使用者为止。 format,shape,strides,suboffsets和internal对使用者是只读的。
PyBuffer_FillInfo()提供了一种在正确处理所有请求类型时公开简单字节缓冲区的简便方法。
PyObject_GetBuffer()是包装此Function的使用者接口。
- releasebufferproc
PyBufferProcs.bf_releasebuffer
- 该函数的签名是:
void (PyObject *exporter, Py_buffer *view);
处理释放缓冲区资源的请求。如果不需要释放资源,则PyBufferProcs.bf_releasebuffer可以是NULL
。否则,此Function的标准实现将采取以下可选步骤:
减少 Export 数量的内部计数器。
如果计数器为
0
,则释放与* view *关联的所有内存。
导出程序必须使用internal字段来跟踪特定于缓冲区的资源。保证该字段保持不变,而使用者可以传递原始缓冲区的副本作为* view *参数。
此函数不得减view->obj
,因为它是在PyBuffer_Release()中自动完成的(此方案对于break参考循环很有用)。
PyBuffer_Release()是包装此Function的使用者接口。
异步对象结构
3.5 版中的新Function。
PyAsyncMethods
- 该结构包含指向实现awaitable和asynchronous iterator对象所需的Function的指针。
这是结构定义:
typedef struct {
unaryfunc am_await;
unaryfunc am_aiter;
unaryfunc am_anext;
} PyAsyncMethods;
- unaryfunc
PyAsyncMethods.am_await
- 该函数的签名是:
PyObject *am_await(PyObject *self);
返回的对象必须是迭代器,即PyIter_Check()必须为其返回1
。
如果对象不是awaitable,则可以将此插槽设置为NULL
。
- unaryfunc
PyAsyncMethods.am_aiter
- 该函数的签名是:
PyObject *am_aiter(PyObject *self);
必须返回awaitable对象。有关详情,请参见anext()。
如果对象未实现异步迭代协议,则可以将此插槽设置为NULL
。
- unaryfunc
PyAsyncMethods.am_anext
- 该函数的签名是:
PyObject *am_anext(PyObject *self);
必须返回awaitable对象。有关详情,请参见anext()。该插槽可以设置为NULL
。
插槽类型 typedefs
- PyObject *
(*allocfunc)
(PyTypeObject ** cls *,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。
- 此Function的目的是将内存分配与内存初始化分开。它应返回一个指向实例的足够长的内存块的指针,该内存块适当对齐,并初始化为零,但将
该函数不应进行任何其他实例初始化,甚至不分配额外的内存。应该由tp_new完成。
无效
(*destructor)
(PyObject ***)PyObject *
(*vectorcallfunc)
(PyObject *callable ,PyObject * const args *,size_t * nargsf *,PyObject ** kwnames *)- See tp_vectorcall_offset.
vectorcallfunc
的参数与_PyObject_Vectorcall()的参数相同。
3.8 版的新Function。
无效
(*freefunc)
(void ***)- See tp_free.
PyObject *
(*newfunc)
( PyObject * , PyObject * , PyObject * )- See tp_new.
int
(*initproc)
(PyObject *,PyObject *,PyObject ***)- See tp_init.
PyObject *
(*reprfunc)
( PyObject * )- See tp_repr.
PyObject *
(*getattrfunc)
(PyObject *self ,char attr *)- 返回对象的命名属性的值。
int
(*setattrfunc)
(PyObject *self ,char attr *,PyObject ** value *)- 设置对象的命名属性的值。 value 参数设置为
NULL
以删除该属性。
- 设置对象的命名属性的值。 value 参数设置为
PyObject *
(*getattrofunc)
(PyObject *self ,PyObject attr *)- 返回对象的命名属性的值。
See tp_getattro.
- int
(*setattrofunc)
(PyObject *self ,PyObject attr *,PyObject ** value *)- 设置对象的命名属性的值。 value 参数设置为
NULL
以删除该属性。
- 设置对象的命名属性的值。 value 参数设置为
See tp_setattro.
PyObject *
(*descrgetfunc)
( PyObject * , PyObject * , PyObject * )- 参见
tp_descrget
。
- 参见
int
(*descrsetfunc)
(PyObject *,PyObject *,PyObject ***)- 参见
tp_descrset
。
- 参见
Py_hash_t
(*hashfunc)
(PyObject ***)- See tp_hash.
PyObject *
(*richcmpfunc)
(PyObject *,PyObject *,int)- See tp_richcompare.
PyObject *
(*getiterfunc)
( PyObject * )- See tp_iter.
PyObject *
(*iternextfunc)
( PyObject * )- See tp_iternext.
Py_ssize_t
(*lenfunc)
(PyObject ***)PyObject *
(*ternaryfunc)
( PyObject * , PyObject * , PyObject * )int
(*ssizeobjargproc)
(PyObject ***,Py_ssize_t)
Examples
以下是 Python 类型定义的简单示例。它们包括您可能会遇到的常见用法。有些人表现出棘手的极端情况。有关更多示例,实用信息和教程,请参见定义扩展类型:教程和定义扩展类型:各种主题。
基本静态类型:
typedef struct {
PyObject_HEAD
const char *data;
} MyObject;
static PyTypeObject MyObject_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mymod.MyObject",
.tp_basicsize = sizeof(MyObject),
.tp_doc = "My objects",
.tp_new = myobj_new,
.tp_dealloc = (destructor)myobj_dealloc,
.tp_repr = (reprfunc)myobj_repr,
};
您可能还会发现带有更详细的初始化程序的旧代码(尤其是在 CPython 代码库中):
static PyTypeObject MyObject_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"mymod.MyObject", /* tp_name */
sizeof(MyObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)myobj_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
(reprfunc)myobj_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
0, /* tp_flags */
"My objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
myobj_new, /* tp_new */
};
支持弱引用,实例字典和哈希的类型:
typedef struct {
PyObject_HEAD
const char *data;
PyObject *inst_dict;
PyObject *weakreflist;
} MyObject;
static PyTypeObject MyObject_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mymod.MyObject",
.tp_basicsize = sizeof(MyObject),
.tp_doc = "My objects",
.tp_weaklistoffset = offsetof(MyObject, weakreflist),
.tp_dictoffset = offsetof(MyObject, inst_dict),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
.tp_new = myobj_new,
.tp_traverse = (traverseproc)myobj_traverse,
.tp_clear = (inquiry)myobj_clear,
.tp_alloc = PyType_GenericNew,
.tp_dealloc = (destructor)myobj_dealloc,
.tp_repr = (reprfunc)myobj_repr,
.tp_hash = (hashfunc)myobj_hash,
.tp_richcompare = PyBaseObject_Type.tp_richcompare,
};
不能被子类化并且不能被调用以创建实例的 str 子类(例如,使用单独的工厂函数):
typedef struct {
PyUnicodeObject raw;
char *extra;
} MyStr;
static PyTypeObject MyStr_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mymod.MyStr",
.tp_basicsize = sizeof(MyStr),
.tp_base = NULL, // set to &PyUnicode_Type in module init
.tp_doc = "my custom str",
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = NULL,
.tp_repr = (reprfunc)myobj_repr,
};
最简单的静态类型(具有固定长度的实例):
typedef struct {
PyObject_HEAD
} MyObject;
static PyTypeObject MyObject_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mymod.MyObject",
};
最简单的静态类型(具有可变长度的实例):
typedef struct {
PyObject_VAR_HEAD
const char *data[1];
} MyObject;
static PyTypeObject MyObject_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mymod.MyObject",
.tp_basicsize = sizeof(MyObject) - sizeof(char *),
.tp_itemsize = sizeof(char *),
};