Python基础 | 1.2PyTypeObject结构详解
(本篇博客是1.Python对象初探——>二、Python对象初探——>3.类型对象——>a.类型对象 _typeobject的扩展内容)
1.PyTypeObject结构源码展示
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;
2.PyTypeObject结构源码逐行分析
下面我们将其分为X个部分进行分析
(1)第一部分:
a.代码展示:
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 */
...
...
} PyTypeObject;
b.分析:
1)PyObject_VAR_HEAD
PyObject_VAR_HEAD定义所有可变大小容器对象的初始段。 这些以“声明具有1个元素的数组”为结尾,但是已经分配了足够的空间,因此该数组实际上有空间容纳ob_size元素。 请注意,ob_size是元素计数,不一定是字节计数。
- 首先我们可以看到,类型对象结构扩展了PyVarObject结构。下面是PyVarObject结构的再次介绍,关于PyObject、PyVarObject详细请点击
- Py_ssize_t PyObject.ob_refcnt
- 引用计数
- PyTypeObject* PyObject.ob_type
- 这是类型的类型,即它的元类型。 它由PyObject_HEAD_INIT宏的参数初始化,并且其值通常应为&PyType_Type。
- Py_ssize_t PyVarObject.ob_size
- 对于静态分配的类型对象,应将其初始化为零。 对于动态分配的类型对象,此字段具有特殊的内部含义。
- ob_size字段用于动态类型(由type_new()创建,通常从类语句中调用)。请注意,PyType_Type(元类型)会初始化tp_itemsize,这意味着其实例(即类型对象)必须具有ob_size字段。【类型对象 是 PyType_Type(元类型) 的实例化。】
- Py_ssize_t PyObject.ob_refcnt
2)const char *tp_name
- [通俗理解]类型名,tp_name,主要是Python内部以及调试的时候使用。
- [官方解释]指向包含类型名称的NUL终止字符串的指针。
- 对于可以作为模块全局变量访问的类型,字符串应为完整的模块名称,后跟一个点,然后是类型名称;对于内置类型,它应该只是类型名称。如果模块是软件包的子模块,则完整的软件包名称是完整模块名称的一部分。
- 如:在包P的子包Q中的模块M中定义的名为T的类型应具有tp_name初始化程序“ P.Q.M.T”。
- 对于可以作为模块全局变量访问的类型,字符串应为完整的模块名称,后跟一个点,然后是类型名称;对于内置类型,它应该只是类型名称。如果模块是软件包的子模块,则完整的软件包名称是完整模块名称的一部分。
- 该字段不能为NULL。它是PyTypeObject()中唯一需要的字段(潜在的tp_itemsize除外)。
3)Py_ssize_t tp_basicsize, tp_itemsize 计算类型实例的字节大小
- tp_basicsize 与 tp_itemsize
- tp_basicsize 可以认为是 “类型实例”的基本大小
- tp_itemsize 可以认为是 元素大小
- 关于定长对象和变长对象(这些类型对象都是“类型的类型”的实例)
- 对于定长对象:
- 定长对象的大小相同,由 tp_basicsize 直接给出。
- 定长对象的 tp_itemsize 为 0.
- 对于变长对象:
- N表示为对象的长度,N的值保存在实例 ob_size 中。
- 变长对象的的大小为:tp_basicsize + N * tp_itemsize。
- 我的理解,如果错误,希望您的指出。
- 对于定长对象,以int举例:
import sys a = 100 print("定长对象int(100)的大小:",sys.getsizeof(a)) # 输出结果为 定长对象int(100)的大小:28 # 对于结果的分析: 对于int类型(在数值大小小于 2^30 - 1 时),tp_basicsize = 28字节,tp_itemsize = 0
- 对于变长对象,以string为例:
import sys a = 'a' ab = 'ab' print("变长对象str('a')的大小:",sys.getsizeof(a)) print("变长对象str('ab')的大小:",sys.getsizeof(ab)) # 输出结果为: 变长对象str('a')的大小:50 变长对象str('ab')的大小:51 # 对于结果的分析: 对于string类型,tp_basicsize = 49;tp_itemsize = 1 当 a = 'a'时,N(元素个数,存储在ob_size实例中)为1,因此,此时 sizeof('a') = 49 + 1 * 1 = 50; 当 a = 'ab'时,N(元素个数,存储在ob_size实例中)为2,因此,此时 sizeof('ab') = 49 + 2 * 1 = 51;
- 对于定长对象,以int举例:
- 对于定长对象:
(2)第二部分:
a.代码展示:
typedef struct _typeobject {
...
/* 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;
...
} PyTypeObject;
b.分析:
1)destructor tp_dealloc
- 指向实例析构函数的指针。除非该类型保证其实例永远不会被释放,否则必须定义此函数。
void tp_dealloc(PyObject *self);
- 当新引用计数为零时,Py_DECREF()和Py_XDECREF()宏将调用析构函数。此时,该实例仍然存在,但没有引用。
- 析构函数应释放实例所拥有的所有引用,释放实例所拥有的所有内存缓冲区(使用与用于分配缓冲区的分配函数相对应的释放函数),并调用类型的tp_free函数。
- 如果类型不是子类型(没有设置Py_TPFLAGS_BASETYPE标志位),则可以直接调用对象解除分配器,而不是通过tp_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); }
2)Py_ssize_t tp_vectorcall_offset
- 每个实例函数的可选偏移量,该偏移量用于实现使用vectorcall协议调用对象的方法,这是更简单的tp_call的更有效的替代方法。
- 仅当设置标志_Py_TPFLAGS_HAVE_VECTORCALL时才使用此字段。 如果是这样,则它必须是一个正整数,其中包含vectorcallfunc指针实例中的偏移量。
- vectorcallfunc指针可以为零,在这种情况下,实例的行为就像未设置_Py_TPFLAGS_HAVE_VECTORCALL一样:调用实例将回退到tp_call。
3)getattrfunc tp_getattr
- 指向get-attribute-string函数的可选指针。
- 不建议使用此字段。 定义后,它应指向一个功能与tp_getattro函数相同的函数,但要使用C字符串而不是Python字符串对象来提供属性名称。
4)setattrfunc tp_setattr
- 指向用于设置和删除属性的函数的可选指针。
- 不建议使用此字段。 定义后,它应指向一个功能与tp_setattro函数相同的函数,但要使用C字符串而不是Python字符串对象来提供属性名称。
5)PyAsyncMethods *tp_as_async
- 指向一个附加结构的指针,该结构包含仅与在C级实现等待和异步迭代器协议的对象有关的字段。
- 3.5版的新功能:以前称为tp_compare和tp_reserved。
6)reprfunc tp_repr
- 指向实现内置函数repr()的函数的可选指针。
(3)第三部分:
a.代码展示:
typedef struct _typeobject {
...
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
...
} PyTypeObject;
b.分析:
1)PyNumberMethods *tp_as_number
- 指向另一个结构的指针,该结构包含仅与实现数字协议的对象相关的字段。 这些字段记录在“Number Object Structures”中。
- [通俗理解]对象如果被认为是数值对象,那么这个对象应该支持哪些操作,每种操作的具体实现是什么样的。
2)PySequenceMethods *tp_as_sequence
- 指向另一个结构的指针,该结构包含仅与实现序列协议的对象相关的字段。 这些字段记录在“Sequence Object Structures”中。
- [通俗理解]如果一个对象被认为是一个序列对象,那么应该支持哪些操作。
3)PyMappingMethods *tp_as_mapping
- 指向包含仅与实现映射协议的对象相关的字段的附加结构的指针。 这些字段记录在“Mapping Object Structures”中。
- [通俗理解]如果一个对象被认为是映射对象,那么应该支持哪些操作。(映射对象:通过名字来引用值的数据结构。例如:字典)
(4)第四部分:
a.代码展示:
typedef struct _typeobject {
...
/* More standard operations (here for binary compatibility) */
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
...
} PyTypeObject;
b.分析:
1)hashfunc tp_hash
- 指向实现内置函数hash()的函数的可选指针。
- 签名与PyObject_Hash()相同:
Py_hash_t tp_hash(PyObject *);
- 值-1不应作为常规返回值返回;当在哈希值的计算过程中发生错误时,该函数应设置一个异常并返回-1。
- 如果未设置此字段(并且未设置tp_richcompare),则尝试获取对象的哈希值会引发TypeError。这与将其设置为PyObject_HashNotImplemented()相同。
- 可以将该字段显式设置为PyObject_HashNotImplemented(),以阻止哈希方法从父类型继承。在Python级别上,这被解释为等效于__hash__ = None,从而导致isinstance(o,collections.Hashable)正确返回False。请注意,相反情况也是如此-在python级别的类上设置__hash__ = None将导致tp_hash插槽设置为PyObject_HashNotImplemented()。
- 签名与PyObject_Hash()相同:
2)ternaryfunc tp_call
- 指向实现调用对象的函数的可选指针。 如果对象不可调用,则应为NULL。
- 签名与PyObject_Call()相同:
PyObject *tp_call(PyObject *self, PyObject *args, PyObject *kwargs);
- 签名与PyObject_Call()相同:
3)reprfunc tp_str
- 指向实现内置操作str()的函数的可选指针。(请注意,str现在是一种类型,str()调用该类型的构造函数。此构造函数调用PyObject_Str()进行实际工作,而PyObject_Str()将调用此处理程序。)
- 签名与PyObject_Str()相同:
PyObject * tp_str(PyObject * self);
- 该函数必须返回字符串或Unicode对象。 它应该是对象的“友好”字符串表示形式,因为它是print()函数将使用的表示形式。
- 签名与PyObject_Str()相同:
4)getattrofunc tp_getattro
- 指向get-attribute函数的可选指针。
- 签名与PyObject_GetAttr()相同:
PyObject * tp_getattro(PyObject * self,PyObject * attr);
- 通常将此字段设置为PyObject_GenericGetAttr()很方便,该方法实现了查找对象属性的常规方法。
- 签名与PyObject_GetAttr()相同:
5)setattrofunc tp_setattro
- 指向用于设置和删除属性的函数的可选指针。
- 签名与PyObject_SetAttr()相同:
PyObject * tp_setattro(PyObject * self,PyObject * attr,PyObject * value);
- 另外,必须支持将值设置为NULL以删除属性。 通常将此字段设置为PyObject_GenericSetAttr()很方便,这实现了设置对象属性的常规方法。
- 签名与PyObject_SetAttr()相同:
(5)第五部分:
a.代码展示:
typedef struct _typeobject {
...
/* 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;
...
} PyTypeObject;
b.分析:
1)PyBufferProcs *tp_as_buffer
- 指向包含仅与实现缓冲区接口的对象相关的字段的附加结构的指针。 这些字段记录在“缓冲区对象结构”中。
2)unsigned long 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值。
// 标志位有: Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_BASETYPE Py_TPFLAGS_READY Py_TPFLAGS_READYING Py_TPFLAGS_HAVE_GC Py_TPFLAGS_DEFAULT Py_TPFLAGS_METHOD_DESCRIPTOR 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 Py_TPFLAGS_HAVE_FINALIZE _Py_TPFLAGS_HAVE_VECTORCALL
3)const char *tp_doc
- 指向NUL终止的C字符串的可选指针,为该类型对象提供docstring。 这在类型和类型实例上作为__doc__属性公开。
4)traverseproc tp_traverse
- 指向垃圾收集器遍历函数的可选指针。 仅在Py_TPFLAGS_HAVE_GC标志位置1时使用。
- 标志位置1时使用。 签名是:
int tp_traverse(PyObject * self,visitproc访问,void * arg);
- 垃圾回收器使用tp_traverse指针检测参考周期。 tp_traverse函数的典型实现只是在实例的每个实例成员(它们是实例拥有的Python对象)上调用Py_VISIT()。
- 标志位置1时使用。 签名是:
5)inquiry tp_clear
- 指向垃圾回收器的清除函数的可选指针。仅在Py_TPFLAGS_HAVE_GC标志位置1时使用。
- 签名是:
int tp_clear(PyObject *);
- tp_clear成员函数用于中断垃圾收集器检测到的循环垃圾中的参考周期。总之,系统中的所有tp_clear函数必须结合起来才能中断所有参考周期。这是微妙的,如果有任何疑问,请提供tp_clear函数。例如,元组类型不实现tp_clear函数,因为有可能证明没有参考周期可以完全由元组组成。因此,其他类型的tp_clear函数必须足以中断包含元组的任何循环。这不是立即显而易见的,很少有充分的理由避免实施tp_clear。
- tp_clear的实现应删除实例对其可能是Python对象的成员的引用,并将其指向那些成员的指针设置为NULL。
- 应该使用Py_CLEAR()宏,因为清除引用非常困难:在将包含对象的指针设置为NULL之前,不得减少对包含对象的引用。这是因为减少引用计数可能会导致所包含的对象成为垃圾,从而触发一连串的回收活动,该活动可能包括调用任意Python代码(由于与所包含的对象相关联的终结器或weakref回调)。如果此类代码有可能再次引用self,那么在那时指向所包含对象的指针为NULL十分重要,这样self就知道不再可以使用所包含的对象。 Py_CLEAR()宏以安全顺序执行操作。
- 由于tp_clear函数的目的是打破参考周期,因此不必清除无法参与参考周期的包含对象,例如Python字符串或Python整数。另一方面,清除所有包含的Python对象并编写该类型的tp_dealloc函数以调用tp_clear可能会很方便。
- 签名是:
6)richcmpfunc tp_richcompare
- 从函数返回Py_True或Py_False,具体取决于比较结果。 VAL_A和VAL_B必须可由C比较运算符排序(例如,它们可以是C int或float)。 第三个参数指定请求的操作,与PyObject_RichCompare()相同。
- 返回值的参考计数已正确增加。
- 出错时,设置一个异常并从函数返回NULL。
7)Py_ssize_t tp_weaklistoffset
- 如果该类型的实例是弱引用的,则此字段大于零,并且包含弱引用列表头的实例结构中的偏移量(如果存在,则忽略GC头);此偏移量由PyObject_ClearWeakRefs()和PyWeakref _ *()函数使用。实例结构需要包括一个PyObject *类型的字段,该字段被初始化为NULL。
- 不要将此字段与tp_weaklist混淆。这是对类型对象本身的弱引用的列表头。
8)getiterfunc tp_iter
- 指向函数的可选指针,该函数返回对象的迭代器。 它的存在通常表示此类型的实例是可迭代的(尽管如果没有此功能,序列可能是可迭代的)。
- 此函数与PyObject_GetIter()具有相同的签名:
PyObject *tp_iter(PyObject *self);
- 此函数与PyObject_GetIter()具有相同的签名:
9)iternextfunc tp_iternext
- 指向函数的可选指针,该函数返回迭代器中的下一项。
- 签名是:
PyObject * tp_iternext(PyObject * self);
- 当迭代器用尽时,它必须返回NULL;否则,它必须返回NULL。 可能会或可能不会设置StopIteration异常。 当发生另一个错误时,它也必须返回NULL。 它的存在表明该类型的实例是迭代器。
- 迭代器类型还应该定义tp_iter函数,并且该函数应返回迭代器实例本身(而不是新的迭代器实例)。
- 此函数具有与PyIter_Next()相同的签名。
- 签名是:
(6)第六部分:
a.代码展示:
typedef struct _typeobject {
...
/* 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;
...
} PyTypeObject;
b.分析:
1)struct PyMethodDef *tp_methods
- 指向以NULL结尾的静态PyMethodDef结构数组的可选指针,声明此类型的常规方法。
- 对于数组中的每个条目,都会将一个条目添加到包含方法描述符的类型的字典中(请参见下面的tp_dict)。
2)struct PyMemberDef *tp_members
- 指向以NULL终止的静态PyMemberDef结构的数组的可选指针,声明此类型实例的常规数据成员(字段或插槽)。
- 对于数组中的每个条目,都会将一个条目添加到包含成员描述符的类型的字典中(请参见下面的tp_dict)。
3)struct PyGetSetDef *tp_getset
- 指向以NULL结尾的静态PyGetSetDef结构数组的可选指针,声明此类型实例的计算属性。
- 对于数组中的每个条目,都会将一个条目添加到包含getset描述符的类型的字典中(请参见下面的tp_dict)。
4)struct _typeobject *tp_base
- 指向从其继承类型属性的基本类型的可选指针。在此级别上,仅支持单继承。多重继承要求通过调用元类型来动态创建类型对象。
- 注意插槽初始化须遵守初始化全局变量的规则。 C99要求初始化器为“地址常数”。诸如PyType_GenericNew()之类的函数指示符(通过隐式转换为指针)是有效的C99地址常量。
但是,不需要使用应用于PyBaseObject_Type()之类的非静态变量的一元“&”运算符即可生成地址常数。编译器可能支持(gcc支持),MSVC不支持。两种编译器在此特定行为上均严格遵循标准。 - 因此,应该在扩展模块的init函数中设置tp_base。
- 注意插槽初始化须遵守初始化全局变量的规则。 C99要求初始化器为“地址常数”。诸如PyType_GenericNew()之类的函数指示符(通过隐式转换为指针)是有效的C99地址常量。
5)PyObject *tp_dict
- 类型的字典通过PyType_Ready()存储在此处。
- 在调用PyType_Ready之前,通常应将此字段初始化为NULL; 它也可以初始化为包含该类型的初始属性的字典。 一旦PyType_Ready()初始化了类型,该类型的额外属性只有在它们不对应于重载操作(例如__add __())时才可以添加到该字典中。
6)descrgetfunc tp_descr_get
- 指向“descriptor get”功能的可选指针。
- 函数签名为:
PyObject * tp_descr_get(PyObject *self, PyObject *obj, PyObject *type);
- 函数签名为:
7)descrsetfunc tp_descr_set
- 指向用于设置和删除描述符值的函数的可选指针。
- 函数签名为:
int tp_descr_set(PyObject *self, PyObject *obj, PyObject *value);
- value参数设置为NULL以删除该值。
- 函数签名为:
8)Py_ssize_t tp_dictoffset
- 如果此类型的实例具有包含实例变量的字典,则此字段为非零值,并且包含实例变量字典类型的实例中的偏移量;否则为0。此偏移量由PyObject_GenericGetAttr()使用。
- 不要将此字段与tp_dict混淆;那是类型对象本身的属性的字典。
- 如果此字段的值大于零,则它指定从实例结构开始的偏移量。如果该值小于零,则指定距实例结构末端的偏移量。负偏移量使用起来更昂贵,并且仅当实例结构包含可变长度部分时才应使用。例如,这用于将实例变量字典添加到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()为您完成的。)
9)initproc tp_init
- 实例初始化函数的可选指针。
- 此函数对应于类的__init __()方法。像__init __()一样,可以在不调用__init __()的情况下创建实例,并且可以通过再次调用其__init __()方法来重新初始化实例。
- 函数签名为:
int tp_init(PyObject *self, PyObject *args, PyObject *kwds);
- self参数是要初始化的实例; args和kwds参数表示对__init __()的调用的位置参数和关键字参数。
- 当类型的tp_new函数返回该类型的实例后,通过调用其类型正常创建实例时,会调用tp_init函数(如果不为NULL)。如果tp_new函数返回的不是原始类型的子类型的某个其他类型的实例,则不会调用tp_init函数;否则,它将被调用。如果tp_new返回原始类型的子类型的实例,则调用该子类型的tp_init。
- 成功则返回0,-1则返回错误。
- 函数签名为:
10)allocfunc tp_alloc
- 指向实例分配功能的可选指针。
- 函数签名为:
PyObject * tp_alloc(PyTypeObject * self,Py_ssize_t nitems);
- 函数签名为:
11)newfunc tp_new
- 指向实例创建函数的可选指针。
- 函数签名为:
PyObject *tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds);
- subtype参数是要创建的对象的类型。 args和kwds参数表示对该类型的调用的位置参数和关键字参数。 请注意,子类型不必等于其tp_new函数被调用的类型。 它可能是该类型的子类型(但不是无关类型)。
- tp_new函数应调用subtype-> tp_alloc(subtype,nitems)为对象分配空间,然后仅在绝对必要的情况下进行更多的初始化。 可以安全地忽略或重复进行的初始化应放在tp_init处理程序中。 一个好的经验法则是,对于不可变类型,所有初始化都应在tp_new中进行,而对于可变类型,大多数初始化应推迟到tp_init中进行。
- 函数签名为:
12)freefunc tp_free
- 实例释放函数的可选指针。
- 它的签名是:
void tp_free(void *self);
- 与此签名兼容的初始化器为PyObject_Free()。
- 它的签名是:
13)inquiry tp_is_gc
- 指向垃圾回收器调用的函数的可选指针。
- 垃圾收集器需要知道特定对象是否可收集。 通常,查看对象类型的tp_flags字段并检查Py_TPFLAGS_HAVE_GC标志位就足够了。 但是某些类型混合了静态和动态分配的实例,并且静态分配的实例是不可收集的。 这些类型应定义此功能。 对于可收集的实例,它应该返回1;对于不可收集的实例,它应该返回0。
- 签名是:
int tp_is_gc(PyObject * self);
- (这种情况的唯一示例就是类型本身。元类型PyType_Type定义了此函数,以区分静态分配类型和动态分配类型。)
- 签名是:
14)PyObject *tp_bases
- 基本类型的元组。
- 这是为由类语句创建的类型设置的。 对于静态定义的类型,它应该为NULL。
15)PyObject *tp_mro
- 在方法解析顺序中,元组包含扩展的基本类型集,从基本类型本身开始到对象结束。
16)PyObject *tp_cache
- 没用过。 仅供内部使用。
17)PyObject *tp_subclasses
- 对子类的弱引用列表。 仅供内部使用。
18)PyObject *tp_weaklist
- 弱引用列表头,用于对此类型对象的弱引用。 不继承。 仅供内部使用。
19)destructor tp_del
- 不建议使用此字段。 请改用tp_finalize。
(7)第七部分:
a.代码展示:
typedef struct _typeobject {
...
...
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
destructor tp_finalize;
} PyTypeObject;
b.分析:
1)unsigned int tp_version_tag
- 用于索引方法缓存。 仅供内部使用。
2)destructor tp_finalize
- 实例完成函数的可选指针。
- 它的签名是:
void tp_finalize(PyObject *self);
- 如果设置了tp_finalize,则在完成实例时,解释器将调用一次。从垃圾回收器(如果实例是一个隔离的引用周期的一部分)中调用它,或者在对象被释放之前调用它。无论哪种方式,都可以确保在尝试中断参考周期之前调用它,以确保它找到处于健全状态的对象。
- 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标志位设置为1。
- 它的签名是: