类型对象 – 对象实现支持(Python教程)(参考资料)
类型对象
也许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_size
field用于动态类型(由type_new()
创建,通常从类语句调用)。注意PyType_Type
(themetatype)初始化tp_itemsize
,这意味着它的实例(即类型对象)must有ob_size
field.
- PyObject*
PyObject._ob_next
- PyObject *
PyObject._ob_prev
- 这些字段仅在定义宏
Py_TRACE_REFS
时出现。初始化为NULL由PyObject_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上可用的动态可加载扩展模块(至少),编译器会抱怨这不是有效的初始化程序。因此,惯例是通过NULL到PyObject_HEAD_INIT
宏并在执行任何其他操作之前,在模块初始化函数的开头显式初始化该字段。这通常是这样做的:Foo_Type.ob_type = &PyType_Type;
这应该在创建任何类型的实例之前完成.
PyType_Ready()
检查是否ob_type
是NULL,如果是,则将其初始化为ob_type
字段基类的.PyType_Ready()
如果非零,则不会更改此字段.
- 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
字段,具有可变长度实例的类型具有非类型-zerotp_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_size
field).基本大小包括宏
PyObject_HEAD
或PyObject_VAR_HEAD
声明的实例中的字段(无论哪个用于声明实例结构)和如果它们存在,则反过来包括_ob_prev
和_ob_next
字段。这意味着获取tp_basicsize
初始值设定项的唯一正确途径是在用于声明实例布局的结构上使用sizeof
运算符。基本大小不包括GC头大小。这些字段由子类型分别继承。如果基类型具有anon-zero
tp_itemsize
,则将tp_itemsize
设置为子类型中的不同非零值通常是不安全的(尽管这取决于基类型的实现)。关于对齐的注释:如果变量项需要特定的对齐,则应该使用
tp_basicsize
的值来处理。示例:假设一个类型实现double
.tp_itemsize
的数组是sizeof(double)
。程序员有责任tp_basicsize
是sizeof(double)
的倍数(假设这是double
的对齐要求.
- detructor
PyTypeObject.tp_dealloc
- 指向实例析构函数的指针。必须定义此函数,除非类型保证其实例永远不会被释放(如单例
None
和Ellipsis
).析构函数由
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_getattr
和tp_getattro
同时包含子类型tp_getattr
和tp_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_setattr
和tp_setattro
从它的基本类型开始,子类的tp_setattr
和tp_setattro
都是NULL.
- PyAsyncMethods *
tp_as_async
- 指向另一个结构的指针,该结构包含仅与实现等待和异步迭代器 C级协议的对象相关的字段。请参阅异步对象结构了解详情.
新版本3.5:以前称为
tp_compare
和tp_reserved
.
- reprfunc
PyTypeObject.tp_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_richcompare
和tp_hash
,当子类型的tp_richcompare
和tp_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_getattr
和tp_getattro
从它的基类型开始,子类的tp_getattr
和tp_getattro
都是NULL.
- setattrofunc
PyTypeObject.tp_setattro
- 指向设置和删除属性的函数的可选指针.
签名与
PyObject_SetAttr()
相同,但必须支持将v设置为NULL以删除属性。将此字段设置为PyObject_GenericSetAttr()
,它实现了设置对象属性的常规方式.这个字段是由子类型和
tp_setattr
一起继承的:一个子类型同时包含tp_setattr
和tp_setattro
从它的基本类型来看,子类型tp_setattr
和tp_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_traverse
和tp_clear
字段一起继承,即如果Py_TPFLAGS_HAVE_GC
标志位在子类型中清除,则tp_traverse
和tp_clear
子类型中的字段存在且有NULL values.目前定义了以下位掩码;这些可以使用
|
运算符进行OR运算,以形成tp_flags
字段的值。宏PyType_HasFeature()
取一个类型和一个标志值,tp和f,并检查是否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_traverse
和tp_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()
需要visit和arg参数来local_traverse()
才能拥有这些特定名称;不要把它们命名为任何东西.这个领域与
tp_clear
和Py_TPFLAGS_HAVE_GC
标志位:标志位,tp_traverse
和tp_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_traverse
和tp_clear
都是从基类型继承而来的如果他们在子类型中都是零.
- richcmpfunc
PyTypeObject.tp_richcompare
- 指向富比较函数的可选指针,其签名为
PyObject *tp_richcompare(PyObject *a, PyObject *b, int op)
。第一个参数保证是由PyTypeObject
.该函数应该返回比较结果(通常是
Py_True
或Py_False
)。如果比较未定义,则必须返回Py_NotImplemented
,如果发生另一个错误则必须返回NULL
并设置例外情况.注意
如果你想实现一种只有一组有限的比较有意义的类型(例如
==
和!=
, 但不是<
andfriends),直接举起TypeError
在丰富的比较功能中该字段由子类型和
tp_hash
:子类继承tp_richcompare
和tp_hash
当子类tp_richcompare
和tp_hash
是NULL.以下常量被定义为
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_True
或Py_False
从函数中,取决于比较的结果.VAL_A和VAL_B必须由C比较运算符订购(例如,它们可以是C int或浮点数)。第三个参数指定了所请求的操作,如PyObject_RichCompare()
.返回值的引用计数正确递增.
出错时,设置异常并从函数返回NULL。
在版本3.7.
- PyObject *
- 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;一个
StopIteration
exception可能设置也可能不设置。当发生另一个错误时,它必须返回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);
- Py_ssize_t
PyTypeObject.tp_dictoffset
- 如果此类型的实例具有包含实例变量的字典,则此字段为非零且包含实例变量字典类型的实例;这个偏移用于
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()
.)这个字段由子类型继承,但请参阅下面列出的规则。子类型可以覆盖此偏移量;这意味着子类型实例将字典存储在与基本类型不同的偏移量处。由于字典总是通过
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参数是要初始化的实例;args和kwds参数表示调用
__init__()
.的位置和关键字参数
tp_init
函数,如果不是NULL,在通常通过调用其类型创建实例时调用,类型为tp_new
functionhas返回了该类型的实例。如果tp_new
函数返回的某个其他类型的实例不是原始类型的子类型,则不调用tp_init
函数;如果tp_new
返回原始类型的子类型的实例,子类型的tp_init
叫做。该字段由子类型继承.
- allocfunc
PyTypeObject.tp_alloc
- 指向实例分配函数的可选指针.
函数签名是
PyObject *tp_alloc(PyTypeObject *self, Py_ssize_t nitems)
此函数的目的是将内存分配与内存初始化分开。它应该返回一个指向实例的足够长度的内存块的指针,适当地对齐,并初始化为零,但是
ob_refcnt
设置为1
和ob_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)
子类型参数是正在创建的对象的类型;args和kwdsarguments表示对type的调用的位置和关键字参数。注意,子类型不必等于调用
tp_new
函数的类型;它可能是该类型的子类型(但不是不相关的类型).tp_new
函数应调用subtype->tp_alloc(subtype, nitems)
为对象分配空间,然后只做同样的事情进一步初始化是绝对必要的。可以安全地签名或重复的初始化应放在tp_init
处理程序中。一个好的例子是,对于不可变类型,所有初始化都应该在tp_new
中进行,而对于可变类型,大多数初始化应该被推迟到tp_init
.这个字段由子类型继承,除非它不是继承的通过静态类型
tp_base
是NULL或&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
- 最大同时分配的对象.
另请注意,在垃圾收集的Python中,tp_dealloc可能被称为fromany Python线程,而不仅仅是创建对象的线程(如果对象成为引用计数循环的一部分,则该循环可能由任何线程上的garbagecollection收集)。对于Python API调用,这不是问题,因为调用tp_dealloc的线程将拥有全局解释器锁(GIL)。但是,如果被销毁的对象反过来从另一个C或C ++库中销毁对象,应该注意确保在调用tp_dealloc的线程上销毁那些对象不会违反库的任何假设.
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;
映射对象结构
- lenfunc
PyMappingMethods.mp_length
- //功能由
PyMapping_Size()
和PyObject_Size()
,并具有相同的签名。此插槽可设置为NULL如果物体没有明确的长度
- binaryfunc
PyMappingMethods.mp_subscript
- 功能由使用
PyObject_GetItem()
和PySequence_GetSlice()
,和PyObject_GetItem()
具有相同的签名。这个槽必须填入PyMapping_Check()
函数才能返回1
,它可以是NULLotherwise.
序列对象结构
PySequenceMethods
- 这个结构保存了一个对象用于实现序列协议的函数的指针.
- lenfunc
PySequenceMethods.sq_length
- 这个函数被
PySequence_Size()
使用PyObject_Size()
,并具有相同的签名。它还用于通过sq_item
和sq_ass_item
slots.
- binaryfunc
PySequenceMethods.sq_concat
- 来处理负索引。//功能由
PySequence_Concat()
并具有相同的标志。它也被+
操作员,通过nb_add
slot.
- ssizeargfunc
PySequenceMethods.sq_repeat
- 功能由使用
PySequence_Repeat()
并具有相同的标志。它也被*
操作符,在通过nb_multiply
slot.
- ssizeargfunc
PySequenceMethods.sq_item
- 功能由使用
PySequence_GetItem()
并具有相同的标志。通过PyObject_GetItem()
插槽尝试订阅后,mp_subscript
也使用它。这个插槽必须填入PySequence_Check()
函数才能返回1
,它可以是NULL否则.负索引处理如下:如果
sq_length
slot isfilled,调用它,序列长度用于计算传递给sq_item
。如果sq_length
是NULL,索引按原样传递给函数.
- 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_add
slot.
- ssizeargfunc
PySequenceMethods.sq_inplace_repeat
- 功能由使用
PySequence_InPlaceRepeat()
并具有相同的标志。它应该修改它的第一个操作数,然后返回它。这个插槽可以留给NULL,在这种情况下PySequence_InPlaceRepeat()
会回到PySequence_Repeat()
。在使用*=
slot.nb_inplace_multiply
缓冲对象结构
进行数字内部乘法后,它也可用于增强的赋值
- getbufferproc
PyBufferProcs.bf_getbuffer
- 这个函数的签名是:
int (PyObject *exporter, Py_buffer *view, int flags);
按exporter的规定处理view的请求填写flags。除了第(3)点,这个函数的实现必须采取以下步骤:
- 检查是否可以满足请求。如果没有,举起
PyExc_BufferError
,设置view->obj
到NULL并返回-1
. - 填写要求的字段.
- 增加一个内部计数器输出的数量.
- 设置
view->obj
到exporter并增加view->obj
. - 返回
0
.
如果exporter是缓冲提供程序的链或树的一部分,可以使用两个主程序:
view的各个字段在缓冲结构,出口商必须如何对特定要求作出反应的规则在缓冲请求类型.
指向的所有记忆
Py_buffer
结构属于导出器,必须保持有效,直到没有消费者为止.format
,shape
,strides
,suboffsets
和internal
对于消费者而言是只读的.PyBuffer_FillInfo()
提供了一种简单的方法来暴露简单的缓冲区,同时正确处理所有请求类型.PyObject_GetBuffer()
是消费者的界面,用于包含这个功能. - 检查是否可以满足请求。如果没有,举起
- releasebufferproc
PyBufferProcs.bf_releasebuffer
- 这个函数的签名是:
void (PyObject *exporter, Py_buffer *view);
处理释放缓冲区资源的请求。如果没有资源需要释放,
PyBufferProcs.bf_releasebuffer
可能是NULL。否则,此函数的标准实现将执行可选步骤:- 减少内部计数器的导出次数.
- 如果计数器是
0
,则释放所有相关的内存使用view.
导出器必须使用
internal
字段来保存特定于缓冲区的资源。保证这个字段保持不变,而消费者可以传递原始缓冲区的副本作为view参数这个函数不能递减
view->obj
,因为这是自动的在PyBuffer_Release()
(这个方案可用于打破参考周期).PyBuffer_Release()
是包含此功能的消费者的接口.
异步对象结构
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()
mustreturn1
为它.如果对象不是NULL,则此插槽可以设置为awaitable .
- unaryfunc
PyAsyncMethods.am_aiter
- 这个功能的签名是:
PyObject *am_aiter(PyObject *self)
必须退回awaitable 宾语。见
__anext__()
详情.此插槽可设置为NULL如果一个对象没有实现异步迭代协议.
评论被关闭。