深入 CPython 类型系统:PyTypeObject 结构与 tp 槽位完全解析
在 CPython 的 C API 中,PyTypeObject 是对象体系的基石:每个 Python 对象的行为(repr、比较、迭代、GC、缓冲导出等)都由其类型对象里的一组 C 函数指针(即 "tp slots")决定。本文基于官方文档 typeobj.rst 并结合 Objects/typeobject.c 与 Include/cpython/object.h 的实际源码,完整梳理 PyTypeObject 的定义、每个槽位的语义与继承规则、tp_flags 位掩码、静态类型与堆类型的差异,以及四个协议扩展结构(number/mapping/sequence/buffer/async),并给出可直接参考的完整类型定义示例。读完后你可以独立编写一个自定义扩展类型的 C 实现。
PyTypeObject 在对象体系中的位置
类型对象本身也是对象,可以用 PyObject_* 或 PyType_* 函数族处理。相比大多数标准类型,类型对象体积较大,原因是它存储了大量 C 函数指针,每个指针实现该类型功能的一小部分。类型对象的字段按结构体中出现的顺序描述,对应 Python 层的行为:例如 tp_repr 实现 repr(),tp_as_number 下的子槽位实现 __add__ 等。
结构体的权威定义位于 Include/cpython/object.h。从源码结构看,该结构体在 tp_vectorcall 之后还包含若干 VM 内部字段(tp_watched、tp_versions_used、_tp_iteritem、_tp_cache),源码中明确注释 "Below here all fields are internal to the VM",扩展模块不应触碰:
// Include/cpython/object.h (节选)
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;
reprfunc tp_repr;
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
PyBufferProcs *tp_as_buffer;
unsigned long tp_flags;
const char *tp_doc;
traverseproc tp_traverse;
inquiry tp_clear;
richcmpfunc tp_richcompare;
Py_ssize_t tp_weaklistoffset;
getiterfunc tp_iter;
iternextfunc tp_iternext;
PyMethodDef *tp_methods;
PyMemberDef *tp_members;
PyGetSetDef *tp_getset;
PyTypeObject *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;
inquiry tp_is_gc;
PyObject *tp_bases;
PyObject *tp_mro;
PyObject *tp_cache;
void *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
unsigned int tp_version_tag;
destructor tp_finalize;
vectorcallfunc tp_vectorcall;
/* Below here all fields are internal to the VM */
unsigned char tp_watched;
uint16_t tp_versions_used;
_Py_iteritemfunc _tp_iteritem;
void *_tp_cache;
};
文档在 "PyTypeObject Definition" 一节中通过 literalinclude 引入了 Doc/includes/typestruct.h,其内容与上面的源码一致。
快速参考:tp 槽位与 Python 特殊方法的映射
下表完整继承自原文档的 "tp slots" 快速参考表。列含义:O = 该槽位在 PyBaseObject_Type 上被设置;T = 在 PyType_Type 上被设置;D = 默认值行为;I = 继承规则。
| 槽位 | 类型 | 特殊方法/属性 | O | T | D | I |
|---|---|---|---|---|---|---|
<R> tp_name |
const char * |
__name__ |
X | X | ||
tp_basicsize |
Py_ssize_t |
X | X | X | ||
tp_itemsize |
Py_ssize_t |
X | X | |||
tp_dealloc |
destructor |
X | X | X | ||
tp_vectorcall_offset |
Py_ssize_t |
X | X | |||
(tp_getattr) |
getattrfunc |
__getattribute__, __getattr__ |
G | |||
(tp_setattr) |
setattrfunc |
__setattr__, __delattr__ |
G | |||
tp_as_async |
PyAsyncMethods * |
sub-slots | % | |||
tp_repr |
reprfunc |
__repr__ |
X | X | X | |
tp_as_number |
PyNumberMethods * |
sub-slots | % | |||
tp_as_sequence |
PySequenceMethods * |
sub-slots | % | |||
tp_as_mapping |
PyMappingMethods * |
sub-slots | % | |||
tp_hash |
hashfunc |
__hash__ |
X | G | ||
tp_call |
ternaryfunc |
__call__ |
X | X | ||
tp_str |
reprfunc |
__str__ |
X | X | ||
tp_getattro |
getattrofunc |
__getattribute__, __getattr__ |
X | X | G | |
tp_setattro |
setattrofunc |
__setattr__, __delattr__ |
X | X | G | |
tp_as_buffer |
PyBufferProcs * |
sub-slots | % | |||
tp_flags |
unsigned long |
X | X | ? | ||
tp_doc |
const char * |
__doc__ |
X | X | ||
tp_traverse |
traverseproc |
X | G | |||
tp_clear |
inquiry |
X | G | |||
tp_richcompare |
richcmpfunc |
__lt__…__ge__ |
X | G | ||
(tp_weaklistoffset) |
Py_ssize_t |
X | ? | |||
tp_iter |
getiterfunc |
__iter__ |
X | |||
tp_iternext |
iternextfunc |
__next__ |
X | |||
tp_methods |
PyMethodDef [] |
X | X | |||
tp_members |
PyMemberDef [] |
X | ||||
tp_getset |
PyGetSetDef [] |
X | X | |||
tp_base |
PyTypeObject * |
__base__ |
X | |||
tp_dict |
PyObject * |
__dict__ |
? | |||
tp_descr_get |
descrgetfunc |
__get__ |
X | |||
tp_descr_set |
descrsetfunc |
__set__, __delete__ |
X | |||
(tp_dictoffset) |
Py_ssize_t |
X | ? | |||
tp_init |
initproc |
__init__ |
X | X | X | |
tp_alloc |
allocfunc |
X | ? | ? | ||
tp_new |
newfunc |
__new__ |
X | X | ? | ? |
tp_free |
freefunc |
X | X | ? | ? | |
tp_is_gc |
inquiry |
X | X | |||
<tp_bases> |
PyObject * |
__bases__ |
~ | |||
<tp_mro> |
PyObject * |
__mro__ |
~ | |||
[tp_cache] |
PyObject * |
|||||
[tp_subclasses] |
void * |
__subclasses__ |
||||
[tp_weaklist] |
PyObject * |
|||||
(tp_del) |
destructor |
|||||
[tp_version_tag] |
unsigned int |
|||||
tp_finalize |
destructor |
__del__ |
X | |||
tp_vectorcall |
vectorcallfunc |
|||||
[tp_watched] |
unsigned char |
标记约定(继承自原文档脚注):
()括号:槽位(事实上)已弃用;<>尖括号:应初始化为NULL并按只读处理(如tp_bases、tp_mro);[]方括号:仅供内部使用;<R>前缀:必填字段,不得为NULL(tp_name)。
D 列符号:X 表示槽位为 NULL 时 PyType_Ready 会填入该值;~ 表示 PyType_Ready 总会设置(该字段应保持 NULL);? 表示 PyType_Ready 视其他槽位而定。I 列符号:X 表示子类型中该槽位为 NULL 时会通过 PyType_Ready 继承基类的值;% 表示子结构体中的槽位逐个继承;G 表示仅与其他槽位组合时继承;? 表示情况复杂,需看具体槽位说明。部分槽位还会通过普通的属性查找链事实上被继承。
子槽位(sub-slots)参考
| 子槽位 | 类型 | 对应特殊方法 |
|---|---|---|
PyAsyncMethods.am_await |
unaryfunc |
__await__ |
PyAsyncMethods.am_aiter |
unaryfunc |
__aiter__ |
PyAsyncMethods.am_anext |
unaryfunc |
__anext__ |
PyAsyncMethods.am_send |
sendfunc |
— |
PyNumberMethods.nb_add |
binaryfunc |
__add__ / __radd__ |
PyNumberMethods.nb_inplace_add |
binaryfunc |
__iadd__ |
PyNumberMethods.nb_subtract |
binaryfunc |
__sub__ / __rsub__ |
PyNumberMethods.nb_inplace_subtract |
binaryfunc |
__isub__ |
PyNumberMethods.nb_multiply |
binaryfunc |
__mul__ / __rmul__ |
PyNumberMethods.nb_inplace_multiply |
binaryfunc |
__imul__ |
PyNumberMethods.nb_remainder |
binaryfunc |
__mod__ / __rmod__ |
PyNumberMethods.nb_inplace_remainder |
binaryfunc |
__imod__ |
PyNumberMethods.nb_divmod |
binaryfunc |
__divmod__ / __rdivmod__ |
PyNumberMethods.nb_power |
ternaryfunc |
__pow__ / __rpow__ |
PyNumberMethods.nb_inplace_power |
ternaryfunc |
__ipow__ |
PyNumberMethods.nb_negative |
unaryfunc |
__neg__ |
PyNumberMethods.nb_positive |
unaryfunc |
__pos__ |
PyNumberMethods.nb_absolute |
unaryfunc |
__abs__ |
PyNumberMethods.nb_bool |
inquiry |
__bool__ |
PyNumberMethods.nb_invert |
unaryfunc |
__invert__ |
PyNumberMethods.nb_lshift / nb_inplace_lshift |
binaryfunc |
__lshift__ / __rlshift__;__ilshift__ |
PyNumberMethods.nb_rshift / nb_inplace_rshift |
binaryfunc |
__rshift__ / __rrshift__;__irshift__ |
PyNumberMethods.nb_and / nb_inplace_and |
binaryfunc |
__and__ / __rand__;__iand__ |
PyNumberMethods.nb_xor / nb_inplace_xor |
binaryfunc |
__xor__ / __rxor__;__ixor__ |
PyNumberMethods.nb_or / nb_inplace_or |
binaryfunc |
__or__ / __ror__;__ior__ |
PyNumberMethods.nb_int |
unaryfunc |
__int__ |
PyNumberMethods.nb_reserved |
void * |
— |
PyNumberMethods.nb_float |
unaryfunc |
__float__ |
PyNumberMethods.nb_floor_divide / nb_inplace_floor_divide |
binaryfunc |
__floordiv__;__ifloordiv__ |
PyNumberMethods.nb_true_divide / nb_inplace_true_divide |
binaryfunc |
__truediv__;__itruediv__ |
PyNumberMethods.nb_index |
unaryfunc |
__index__ |
PyNumberMethods.nb_matrix_multiply / nb_inplace_matrix_multiply |
binaryfunc |
__matmul__ / __rmatmul__;__imatmul__ |
PyMappingMethods.mp_length |
lenfunc |
__len__ |
PyMappingMethods.mp_subscript |
binaryfunc |
__getitem__ |
PyMappingMethods.mp_ass_subscript |
objobjargproc |
__setitem__ / __delitem__ |
PySequenceMethods.sq_length |
lenfunc |
__len__ |
PySequenceMethods.sq_concat |
binaryfunc |
__add__ |
PySequenceMethods.sq_repeat |
ssizeargfunc |
__mul__ |
PySequenceMethods.sq_item |
ssizeargfunc |
__getitem__ |
PySequenceMethods.sq_ass_item |
ssizeobjargproc |
__setitem__ / __delitem__ |
PySequenceMethods.sq_contains |
objobjproc |
__contains__ |
PySequenceMethods.sq_inplace_concat |
binaryfunc |
__iadd__ |
PySequenceMethods.sq_inplace_repeat |
ssizeargfunc |
__imul__ |
PyBufferProcs.bf_getbuffer |
getbufferproc |
__buffer__ |
PyBufferProcs.bf_releasebuffer |
releasebufferproc |
__release_buffer__ |
槽位函数指针类型(slot typedefs)
以下是原文档给出的全部槽位 typedef 及其参数/返回值,编写实现函数时必须严格遵守:
| typedef | 参数类型 | 返回类型 |
|---|---|---|
allocfunc |
PyTypeObject *、Py_ssize_t |
PyObject * |
destructor |
PyObject * |
void |
freefunc |
void * |
void |
traverseproc |
PyObject *、visitproc、void * |
int |
newfunc |
PyTypeObject *、PyObject *、PyObject * |
PyObject * |
initproc |
PyObject *、PyObject *、PyObject * |
int |
reprfunc |
PyObject * |
PyObject * |
getattrfunc |
PyObject *、const char * |
PyObject * |
setattrfunc |
PyObject *、const char *、PyObject * |
int |
getattrofunc |
PyObject *、PyObject * |
PyObject * |
setattrofunc |
PyObject *、PyObject *、PyObject * |
int |
descrgetfunc |
PyObject *、PyObject *、PyObject * |
PyObject * |
descrsetfunc |
PyObject *、PyObject *、PyObject * |
int |
hashfunc |
PyObject * |
Py_hash_t |
richcmpfunc |
PyObject *、PyObject *、int |
PyObject * |
getiterfunc |
PyObject * |
PyObject * |
iternextfunc |
PyObject * |
PyObject * |
lenfunc |
PyObject * |
Py_ssize_t |
getbufferproc |
PyObject *、Py_buffer *、int |
int |
releasebufferproc |
PyObject *、Py_buffer * |
void |
inquiry |
PyObject * |
int |
unaryfunc |
PyObject * |
PyObject * |
binaryfunc |
PyObject *、PyObject * |
PyObject * |
ternaryfunc |
PyObject *、PyObject *、PyObject * |
PyObject * |
ssizeargfunc |
PyObject *、Py_ssize_t |
PyObject * |
ssizeobjargproc |
PyObject *、Py_ssize_t、PyObject * |
int |
objobjproc |
PyObject *、PyObject * |
int |
objobjargproc |
PyObject *、PyObject *、PyObject * |
int |
其中 allocfunc 的契约值得特别注意:它只负责"分内存",不负责"初始化"——应返回长度足够、对齐正确、清零且 ob_refcnt == 1、ob_type 指向类型参数的内存块;若 tp_itemsize 非零,需把 ob_size 初始化为 nitems,内存块长度为 tp_basicsize + nitems * tp_itemsize 向上取整到 sizeof(void*) 的倍数;除此之外不得做任何实例初始化(包括分配额外内存),那属于 tp_new 的职责。
各槽位详解:语义、继承与默认值
PyObject / PyVarObject 部分
PyTypeObject 扩展自 PyVarObject:
-
ob_refcnt:由PyObject_HEAD_INIT宏初始化为 1。注意:静态类型的实例(ob_type指回该类型的对象)不计入对类型的引用;而堆分配类型的实例计入引用。该字段不被子类型继承。 -
ob_type:类型对象自身的元类型,正常应为&PyType_Type。对需要在 Windows 上可动态加载的扩展模块,编译器不允许把&PyType_Type作为静态初始化值,约定是向PyObject_HEAD_INIT传NULL,然后在模块初始化函数开头显式赋值:Foo_Type.ob_type = &PyType_Type;这必须在创建任何实例之前完成。
PyType_Ready会检查ob_type是否为NULL,若是则用基类的ob_type初始化;非零时不改动。该字段被子类型继承。 -
ob_size:静态类型应初始化为 0;堆类型(由type_new创建,通常来自class语句)该字段有特殊的内部含义,必须通过Py_SIZE()/Py_SET_SIZE()宏访问。不被继承。
命名与尺寸:tp_name、tp_basicsize、tp_itemsize
tp_name:唯一必填字段。可被模块全局访问的类型应使用"完整模块名 + 点 + 类型名",如包P中子包Q的模块M里定义的T,tp_name应为"P.Q.M.T"。堆类型只需类型名本身,模块名应显式存入类型字典的'__module__'键。静态类型中tp_name应包含点号:最后一个点之前映射为__module__,之后映射为__name__;若没有点号,整个字段成为__name__且__module__未定义——这会导致类型无法被 pickle,也不会出现在 pydoc 生成的模块文档中。tp_basicsize/tp_itemsize:决定实例字节大小。tp_itemsize为 0 表示定长实例,所有实例大小都是tp_basicsize(可用PyUnstable_Object_GC_NewWithExtraData打破这一规则);非零表示变长实例,实例必须有ob_size字段,大小为tp_basicsize + N * tp_itemsize。PyObject_NewVar会把N存入ob_size。注意两点:ob_size后续可能被挪作他用。例如int实例以实现定义的方式使用ob_size的位;访问底层存储应使用PyLong_Export。list实例是定长的却带有ob_size字段,读长度应调用PyList_Size而非直接读ob_size。- 正确设置
tp_basicsize的方式是对声明实例布局的结构体使用sizeof,该结构体必须包含基类型结构体,因此tp_basicsize必须 ≥ 基类的tp_basicsize。由于每个类型都是object的子类型,结构体必须包含PyObject_HEAD或PyObject_VAR_HEAD。tp_basicsize不包含 GC 头大小;它必须是_Alignof(PyObject)的倍数,若可变部分有特殊对齐要求,tp_basicsize与tp_itemsize都必须是该对齐值的倍数(如可变部分存double,两者都须是_Alignof(double)的倍数)。 - 继承规则:两个字段各自独立继承——置 0 表示"实例不需要额外存储",
PyType_Ready会从基类拷贝值。若基类tp_itemsize非零,子类型一般不应改为另一个非零值。
生命周期槽位:alloc / new / init / dealloc / free / clear / traverse / finalize
tp_alloc(allocfunc):签名 PyObject *tp_alloc(PyTypeObject *self, Py_ssize_t nitems)。静态子类型继承该槽位(继承自 object 时为 PyType_GenericAlloc);堆子类型不继承,恒为 PyType_GenericAlloc。
tp_new(newfunc):签名 PyObject *tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)。subtype 是被创建对象的类型,未必等于提供该 tp_new 的类型本身(可以是其子类型)。实现应调用 subtype->tp_alloc(subtype, nitems) 分配内存,然后只做"绝对必要"的初始化;可以被忽略或重复的初始化放入 tp_init。经验法则:不可变类型的全部初始化放在 tp_new,可变类型的大部分初始化推迟到 tp_init。静态类型无默认值:tp_new 为 NULL 时该类型不能被调用创建实例(通常意味着实例由工厂函数创建)。
tp_init(initproc):签名 int tp_init(PyObject *self, PyObject *args, PyObject *kwds),对应 __init__。当通过调用类型正常创建实例时,在 tp_new 返回该类型的实例后被调用;若 tp_new 返回了其他(非子类型的)类型的实例,则不调用 tp_init;返回子类型实例时调用子类型的 tp_init。成功返回 0,错误返回 -1 并设置异常。
tp_dealloc(destructor):签名 void tp_dealloc(PyObject *self)。职责有三:移除实例拥有的所有引用(如调用 Py_CLEAR)、释放实例拥有的所有内存缓冲、调用 tp_free 释放对象本身。关键约束:
-
若调用了可能设置错误指示器的函数,必须用
PyErr_GetRaisedException/PyErr_SetRaisedException备份和恢复,避免破坏可能已存在的异常:static void foo_dealloc(foo_object *self) { PyObject *exc = PyErr_GetRaisedException(); ... PyErr_SetRaisedException(exc); } -
dealloc 本身不得抛出异常;遇到错误应调用
PyErr_FormatUnraisable记录并清除不可抛异常。 -
建议开头调用
PyObject_CallFinalizerFromDealloc,保证对象在被销毁前总是先被 finalize。 -
若类型支持 GC(
Py_TPFLAGS_HAVE_GC),在清理成员字段前必须先调用PyObject_GC_UnTrack。 -
允许在 dealloc 中调用
tp_clear以减少代码重复,但注意tp_clear可能已被调用过。 -
若类型是堆分配(
Py_TPFLAGS_HEAPTYPE),deallocator 应在调用类型 deallocator 之后释放对类型对象自身的引用(Py_DECREF)。 -
文档给出的完整参考实现:
static void foo_dealloc(PyObject *self) { PyObject *exc = PyErr_GetRaisedException(); if (PyObject_CallFinalizerFromDealloc(self) < 0) { // self was resurrected. goto done; } PyTypeObject *tp = Py_TYPE(self); if (tp->tp_flags & Py_TPFLAGS_HAVE_GC) { PyObject_GC_UnTrack(self); } // Optional, but convenient to avoid code duplication. if (tp->tp_clear && tp->tp_clear(self) < 0) { PyErr_WriteUnraisable(self); } // Any additional destruction goes here. tp->tp_free(self); self = NULL; // In case PyErr_WriteUnraisable() is called below. if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) { Py_CLEAR(tp); } done: if (PyErr_Occurred()) { PyErr_WriteUnraisable(self); } PyErr_SetRaisedException(exc); } -
tp_dealloc可能从任意 Python 线程被调用(对象若进入引用环,可能在任意线程的 GC 中被回收)。
tp_free(freefunc):签名 void tp_free(void *self),必须释放 tp_alloc 分配的内存。静态子类型继承该槽位(继承自 object 时为 PyObject_Free;例外:若类型开启 GC 且将继承 PyObject_Free,则改为默认为 PyObject_GC_Del);堆子类型不继承,默认是与 PyType_GenericAlloc 及 GC 标志匹配的释放函数。
tp_traverse(traverseproc):GC 遍历函数,仅在 Py_TPFLAGS_HAVE_GC 设置时生效。与 tp_clear、GC 标志位三件套成组继承:三者全为零时从基类整体继承。
tp_clear(inquiry):签名 int tp_clear(PyObject *)。目的是打断导致孤立环(cyclic isolate)的引用循环,使对象可以被安全销毁。被 clear 的对象处于"部分销毁"状态,不必满足正常使用的不变量。要点:
-
无需要清掉不能参与引用环的引用(字符串、整数),但实践中常清掉所有引用,并让
tp_dealloc复用tp_clear。 -
非平凡的清理应放在
tp_finalize而非tp_clear。 -
若
tp_clear不能打断某个环,环中的对象可能永远不可回收(泄漏),见gc.garbage。 -
被引用对象可能已经被 clear,不保证处于一致状态;
tp_clear可能从任意线程调用。 -
对象不保证在
tp_dealloc前被自动 clear。 -
实现中应把成员引用置
NULL,并使用Py_CLEAR宏——因为引用释放(Py_DECREF)可能在把指针置NULL之前就触发被包含对象的回收链(含 finalizer、weakref 回调等任意 Python 代码),而这些代码可能再次引用self,此时被包含对象的指针必须已经是NULL:static int local_clear(PyObject *op) { localobject *self = (localobject *) op; Py_CLEAR(self->key); Py_CLEAR(self->args); Py_CLEAR(self->kw); Py_CLEAR(self->dict); return 0; } -
若设置了
Py_TPFLAGS_MANAGED_DICT,clear 函数必须调用PyObject_ClearManagedDict((PyObject*)self)。 -
一个微妙但重要的结论:全系统所有
tp_clear函数合起来必须能打断所有引用环。例如tuple类型没有tp_clear,因为可以证明不可能只由 tuple 组成引用环,所以含 tuple 的环由其他类型的tp_clear负责打断。若不确定,就提供tp_clear。
tp_finalize(destructor,3.4 引入):签名 void tp_finalize(PyObject *self),是 __del__ 的 C 实现。finalize 的目的是在对象与它直接或间接引用的对象仍处一致状态时执行非平凡清理,且允许执行任意 Python 代码。约束与保证:
-
Python 自动 finalize 一个对象前,其直接或间接被引用者可能已被 finalize,但都不会被 clear;其他未 finalize 的对象可能还在使用该对象,因此 finalizer 必须把对象留在健全状态。
-
自动 finalize 后,Python 可能开始 clear 该对象及其被引用者,被 clear 的对象不保证一致状态,因此 finalizer 必须能容忍"被清空的被引用者"。
-
对象不保证在
tp_dealloc之前被自动 finalize,建议在 dealloc 开头调用PyObject_CallFinalizerFromDealloc。 -
tp_finalize可从任意线程调用(持 GIL),也可能在解释器关闭、部分全局变量已被删除时调用。 -
自动 finalize 的算法与保证:CPython 目前只把开启 GC 的对象标记为 finalized;finalizer 若使对象重新可达即"复活"对象,取消待销毁流程;Python 不会 finalize 可达对象、不会在 clear 之后 finalize、不会并发 finalize 同一环的两个成员、finalize 一个环的任何成员前会先 finalize 全部成员;要手动 finalize 应调用
PyObject_CallFinalizer/PyObject_CallFinalizerFromDealloc而非直接调槽位。 -
finalizer 应保持不变量:推荐写法是先
PyErr_GetRaisedException备份异常,中间出错用PyErr_WriteUnraisable/PyErr_FormatUnraisable记录,最后PyErr_SetRaisedException恢复:static void foo_finalize(PyObject *self) { PyObject *exc = PyErr_GetRaisedException(); if (do_something_that_might_raise() != success_indicator) { PyErr_WriteUnraisable(self); goto done; } done: PyErr_SetRaisedException(exc); }3.8 起不再需要设置
Py_TPFLAGS_HAVE_FINALIZE才生效。
tp_is_gc(inquiry):签名 int tp_is_gc(PyObject *self)。GC 需要判断某个具体对象是否可收集;通常看 Py_TPFLAGS_HAVE_GC 即可,但静态/动态混合分配实例的类型应提供该函数,可收集返回 1,否则返回 0。唯一使用它的例子是 PyType_Type 本身(区分静态与堆类型)。为 NULL 时 Py_TPFLAGS_HAVE_GC 起等效作用。
属性访问、比较、迭代与调用
tp_getattro/tp_setattro(推荐)与tp_getattr/tp_setattr(已弃用):属性名以 C 字符串还是 Python 字符串对象传递的两种旧接口。两者成组继承:子类型的两个字段都为NULL时才从基类同时继承。默认实现PyObject_GenericGetAttr/PyObject_GenericSetAttr由PyBaseObject_Type提供。setattro必须支持value == NULL表示删除属性。tp_repr:签名同PyObject_Repr,必须返回 str 对象。理想情况下返回值能被eval还原出同值对象;做不到就返回'<...>'形式的可推断字符串。默认输出<%s object at %p>。tp_str:签名同PyObject_Str,应返回"友好"字符串(print使用)。未设置时回退到PyObject_Repr。tp_hash:签名Py_hash_t tp_hash(PyObject *)。-1只能作为错误返回值(需设置异常);未设置且tp_richcompare也未设置时,哈希对象抛TypeError(等价于PyObject_HashNotImplemented)。显式设为PyObject_HashNotImplemented可阻断从父类型继承 hash,等价于 Python 层的__hash__ = None。与tp_richcompare成组继承。tp_richcompare:签名PyObject *tp_richcompare(PyObject *self, PyObject *other, int op)。第一个参数保证是该类型实例。比较未定义时返回Py_NotImplemented;出错返回NULL并设异常。操作常量:Py_LT(<)、Py_LE(<=)、Py_EQ(==)、Py_NE(!=)、Py_GT(>)、Py_GE(>=)。宏Py_RETURN_RICHCOMPARE(VAL_A, VAL_B, op)(3.7+)可简化实现,返回新强引用。PyBaseObject_Type提供默认实现,但若只定义了tp_hash,继承的比较函数也不会被使用。tp_call(ternaryfunc):签名同PyObject_Call;不可调用对象应为NULL。tp_vectorcall/tp_vectorcall_offset:vectorcall 协议(更高效的调用路径)。tp_vectorcall_offset是实例中vectorcallfunc指针字段的偏移,仅在Py_TPFLAGS_HAVE_VECTORCALL设置时使用;指针可为NULL,此时回退到tp_call。设置该标志的类必须同时设置tp_call且行为一致(可设为PyVectorcall_Call)。3.8 之前该槽位叫tp_print;3.12 起,用户类在 Python 层重新赋值__call__会清除Py_TPFLAGS_HAVE_VECTORCALL标志以避免不一致。tp_vectorcall(3.9 起生效,字段 3.8 引入)用于优化类型对象本身的调用(即type.__call__路径):若为NULL则走Py_TYPE(type)->tp_call;它必须与type->tp_call行为一致(默认元类下即:调用type->tp_new,结果若是type的子类型则调用type->tp_init,返回tp_new的结果)。该字段永不继承。tp_iter(getiterfunc)与tp_iternext(iternextfunc):tp_iter存在通常意味着实例可迭代;tp_iternext在迭代器耗尽时必须返回NULL(可以设置也可以不设置StopIteration),其他错误同样返回NULL。迭代器类型还应定义tp_iter并返回迭代器自身而非新迭代器。tp_descr_get/tp_descr_set:描述符协议,签名分别为PyObject *(PyObject *self, PyObject *obj, PyObject *type)与int (PyObject *self, PyObject *obj, PyObject *value)(value == NULL表示删除)。被子类型继承。
方法与成员声明数组
tp_methods(PyMethodDef *):NULL 结尾的静态数组,声明常规方法;每个条目会向类型字典加入一个方法描述符。不被继承(方法通过其他机制继承)。tp_members(PyMemberDef *):声明实例的数据成员;每个条目向类型字典加入成员描述符。不被继承。tp_getset(PyGetSetDef *):声明计算属性;每个条目向类型字典加入 getset 描述符。不被继承。
继承结构与内部槽位
tp_base:C 层仅支持单继承;多继承需要动态创建类型(调用元类型)。由于 C99 地址常量初始化规则(&PyBaseObject_Type这类一元&取址在部分编译器下不构成地址常量,MSVC 不支持),tp_base应在模块 init 函数中设置。默认为&PyBaseObject_Type。不被继承。tp_dict:由PyType_Ready存放类型字典。应在调用PyType_Ready前初始化为NULL(或初始属性字典)。类型初始化完成后只能加入非重载操作的属性,之后应视为只读。不得用PyDict_SetItem等字典 C API 直接修改。某些类型的字典并不存放在该槽位,取任意类型的字典请用PyType_GetDict。3.12 起的内部细节:静态内置类型的tp_dict恒为NULL,其字典改存于PyInterpreterState。tp_dictoffset/tp_weaklistoffset:实例字典与弱引用链表头的实例内偏移,分别被PyObject_GenericGetAttr和PyObject_ClearWeakRefs/PyWeakref_*使用。二者应视为只写——取字典指针用PyObject_GenericGetDict(可能需要分配内存),直接访问属性更建议走PyObject_GetAttr。注意tp_dictoffset(实例字典)与tp_dict(类型自身字典)不要混淆;tp_weaklistoffset与tp_weaklist(对类型对象的弱引用表头)也不要混淆。文档建议优先使用Py_TPFLAGS_MANAGED_DICT/Py_TPFLAGS_MANAGED_WEAKREF替代这两个字段:若设置了对应标志,相应偏移会被设为-1表示"不安全使用";同时设置标志与偏移是错误。子类型不应覆盖tp_dictoffset(C 代码可能按旧偏移访问,不安全),需要支持继承场景时应改用Py_TPFLAGS_MANAGED_DICT。tp_bases/tp_mro:均为只读,初始化为NULL,由PyType_Ready填充(MRO 为以自身开始、object结束的元组)。静态类型使用多继承效果不佳:设置tp_bases元组不会报错,但部分槽位只会从第一个基类继承。tp_cache/tp_subclasses/tp_weaklist/tp_version_tag/tp_watched:均为内部使用。tp_subclasses自 3.12 起类型改为void *(某些类型上不是有效的PyObject*),取子类列表应调用type.__subclasses__();tp_version_tag用于方法缓存索引,3.12 起静态内置类型的弱引用存放在PyInterpreterState上,tp_weaklist对它们恒为NULL。tp_del:已弃用,请使用tp_finalize。
tp_flags 位掩码
tp_flags 是位掩码:部分标志指示特定场景下的变体语义,部分标志表示"某些历史上并非始终存在的字段现在有效"——若标志位未置位,其守护的字段必须视为 0 或 NULL,不得访问。默认值(PyBaseObject_Type)为 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE。检查是否具备某特性用 PyType_HasFeature(tp, f)(即 tp->tp_flags & f)。主要标志及其继承规则:
| 标志 | 含义 | 继承规则 |
|---|---|---|
Py_TPFLAGS_HEAPTYPE |
类型对象本身在堆上分配(如 PyType_FromSpec 创建);实例的 ob_type 视为对类型的引用,实例创建时 INCREF、销毁时 DECREF;堆类型还应支持 GC(可能与其模块对象成环) |
文档标注 "???"(未明确) |
Py_TPFLAGS_BASETYPE |
可作为其他类型的基类;未置位则不可被继承(类似 Java 的 final 类) | "???" |
Py_TPFLAGS_READY |
类型已被 PyType_Ready 完整初始化 |
"???" |
Py_TPFLAGS_READYING |
PyType_Ready 正在初始化该类型 |
"???" |
Py_TPFLAGS_HAVE_GC |
实例支持 GC:tp_alloc 必须用 PyObject_GC_New / PyType_GenericAlloc 分配,tp_free 用 PyObject_GC_Del 释放 |
与 tp_traverse、tp_clear 成组继承:三者全为零时从基类整体继承 |
Py_TPFLAGS_DEFAULT |
所有与"字段存在性"相关位位的合集,当前仅含 Py_TPFLAGS_HAVE_STACKLESS_EXTENSION |
"???" |
Py_TPFLAGS_METHOD_DESCRIPTOR |
对象行为类似未绑定方法(3.8+):meth.__get__(obj, cls)(*args) 等价于 meth(obj, *args);使解释器对 obj.meth() 跳过临时 bound method 对象。对未设 Py_TPFLAGS_IMMUTABLETYPE 的类型永不继承;扩展类型在 tp_descr_get 被继承时随之继承 |
见左 |
Py_TPFLAGS_MANAGED_DICT(3.12+) |
实例有 __dict__ 且由 VM 管理;必须同时设 Py_TPFLAGS_HAVE_GC;traverse 必须调 PyObject_VisitManagedDict,clear 必须调 PyObject_ClearManagedDict |
除非父类设置了 tp_dictoffset |
Py_TPFLAGS_MANAGED_WEAKREF(3.12+) |
实例可被弱引用;必须同时设 Py_TPFLAGS_HAVE_GC |
除非父类设置了 tp_weaklistoffset |
Py_TPFLAGS_PREHEADER(3.12+) |
等价于 `MANAGED_DICT | MANAGED_WEAKREF`;依赖 VM 实现,值不稳定,建议直接使用单个标志 |
Py_TPFLAGS_ITEMS_AT_END(3.12+) |
仅限变长类型:可变部分位于实例内存末尾,偏移为 Py_TYPE(obj)->tp_basicsize;需保证所有超类使用该布局或不是变长的(Python 不检查) |
被继承 |
Py_TPFLAGS_LONG_SUBCLASS / LIST / TUPLE / BYTES / UNICODE / DICT / BASE_EXC / TYPE |
供 PyType_FastSubclass 快速判断是否为内置类型子类型(如 PyLong_Check 调用);比 PyObject_IsInstance 快。继承自内置类型的自定义类型应正确设置这些标志,否则不同检查路径行为会不一致 |
— |
Py_TPFLAGS_HAVE_FINALIZE(3.4,3.8 弃用) |
曾表示 tp_finalize 存在;现解释器假设该槽位始终存在,无需再设 |
— |
Py_TPFLAGS_HAVE_VECTORCALL(3.8 起 _Py_TPFLAGS_HAVE_VECTORCALL,3.9 更名) |
类实现了 vectorcall 协议,见 tp_vectorcall_offset |
当 tp_call 被继承时随之继承;3.12 起类重新赋值 __call__ 时移除,且可变类也可以继承 |
Py_TPFLAGS_IMMUTABLETYPE(3.10+) |
类型对象不可变:不能设置/删除类型属性;PyType_Ready 会自动为静态类型应用 |
不继承 |
Py_TPFLAGS_DISALLOW_INSTANTIATION(3.10+) |
禁止创建实例:tp_new 置 NULL、类型字典不建 __new__ 键。必须在创建类型前(如 PyType_Ready 之前)设置;静态类型若 tp_base 为 NULL/&PyBaseObject_Type 且 tp_new 为 NULL 时自动设置 |
不继承;子类除非提供非 NULL 的 tp_new(仅 C API 可能),否则同样不可实例化。注意:想让基类不可实例化但子类可以(抽象基类),不要用该标志,而应让 tp_new 仅对子类成功 |
Py_TPFLAGS_MAPPING(3.10+) |
实例可作为 match 块的 mapping pattern 主体;注册/继承 collections.abc.Mapping 时自动置位,注册 Sequence 时清除;与 Py_TPFLAGS_SEQUENCE 互斥,同时启用是错误 |
由未设置 SEQUENCE 的类型继承 |
Py_TPFLAGS_SEQUENCE(3.10+) |
实例可作为 match 块的 sequence pattern 主体;与 MAPPING 互斥 |
由未设置 MAPPING 的类型继承 |
Py_TPFLAGS_VALID_VERSION_TAG |
内部标志,勿动;类变更应调用 PyType_Modified |
— |
Py_TPFLAGS_HAVE_VERSION_TAG |
空操作宏(3.13 软弃用),历史上表示 tp_version_tag 可用 |
— |
Py_TPFLAGS_INLINE_VALUES(3.13+) |
实例的 "inline values" 数组(属性存储)紧跟对象末尾;要求设置 Py_TPFLAGS_HAVE_GC |
不继承 |
Py_TPFLAGS_IS_ABSTRACT |
抽象类型,不可实例化(见 abc 模块) |
不继承 |
Py_TPFLAGS_HAVE_STACKLESS_EXTENSION |
内部保留(历史上供 Stackless Python),勿动,可能在未来移除 | — |
PyType_Ready:默认值填充与继承的实际发生地
文档反复提到的 "PyType_Ready 会填充 NULL 槽位、计算 MRO、建立类型字典" 等行为,其实现位于 Objects/typeobject.c。从源码可以确认:
- 公共入口 PyType_Ready:若
tp_flags已有Py_TPFLAGS_READY直接返回 0;否则,非堆类型会被加上Py_TPFLAGS_IMMUTABLETYPE并通过_Py_SetImmortalUntracked置为永生("Static types must be immortal"),然后在类型锁内调用type_ready(type, 1, 1)。 - 内部函数 type_ready 完成继承槽位拷贝、MRO 计算(源码中 MRO 计算路径会调用类型对象的
mro方法)、fixup_slot_dispatchers(把 Python 层定义的特殊方法映射回 C 槽位分发器)等工作,成功后置Py_TPFLAGS_READY。 - 静态内置类型不走
PyType_Ready公开路径,而由init_static_type走type_ready并打上_Py_TPFLAGS_STATIC_BUILTIN标志,这也解释了为什么静态类型的tp_dict/tp_weaklist存放在PyInterpreterState而非结构体内。
因此文档中每个槽位的 "Default" 小节(如 "PyType_Ready 会在其为 NULL 时填入此值")对应的是 type_ready 中的继承与默认逻辑;"Inheritance" 小节描述的是同一流程中对基类槽位的拷贝规则。
静态类型与堆类型
静态类型(static types):C 代码中直接定义静态 PyTypeObject 结构体并用 PyType_Ready 初始化。相对 Python 定义的类型,其限制为:
- 只能有一个基类,不能用多继承;
- 类型对象(其实例不一定)不可变,无法从 Python 增改类型对象属性——实现上
PyType_Ready会自动加上Py_TPFLAGS_IMMUTABLETYPE,与 Objects/typeobject.c 的源码一致; - 静态类型对象跨子解释器共享,不应包含子解释器专属状态(与 Objects/typeobject.c 中
init_static_type按解释器管理弱引用等状态的做法相印证); - 由于
PyTypeObject在 Limited API 中只是不透明结构(参见 Include/pytypedefs.h 的前向声明),使用静态类型的扩展模块必须针对特定 Python 次版本编译。
堆类型(heap types):与 class 语句创建的类一一对应,带有 Py_TPFLAGS_HEAPTYPE 标志。做法是填充 PyType_Spec 结构体并调用 PyType_FromSpec、PyType_FromSpecWithBases、PyType_FromModuleAndSpec 或 PyType_FromMetaclass。
协议扩展结构
数值协议:PyNumberMethods
PyNumberMethods 保存对象实现数值协议的函数指针,各槽位与同名 number 一节 中记录的函数配套使用。结构体定义(继承自原文档):
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(Python 3.0.1 前叫nb_long)。
映射协议:PyMappingMethods
三个成员:
mp_length(lenfunc):被PyMapping_Size和PyObject_Size使用,签名相同;对象无长度时可设为NULL。mp_subscript(binaryfunc):被PyObject_GetItem与PySequence_GetSlice使用;PyMapping_Check返回 1 必须填写该槽位。mp_ass_subscript(objobjargproc):被PyObject_SetItem、PyObject_DelItem、PySequence_SetSlice、PySequence_DelSlice使用;v为NULL表示删除项;NULL表示不支持项的赋值/删除。
序列协议:PySequenceMethods
sq_length(lenfunc):被PySequence_Size/PyObject_Size使用;也用于经sq_item/sq_ass_item处理负索引。sq_concat(binaryfunc):被PySequence_Concat使用;也用于+运算符——在nb_add数值加法尝试失败之后。sq_repeat(ssizeargfunc):被PySequence_Repeat使用;也用于*运算符(在nb_multiply之后)。sq_item(ssizeargfunc):被PySequence_GetItem使用;也被PyObject_GetItem使用(在mp_subscript之后)。PySequence_Check返回 1 必须填写该槽位。负索引处理:若sq_length已填写则用其长度换算为正索引再传给sq_item;否则索引原样传递。sq_ass_item(ssizeobjargproc):被PySequence_SetItem使用;也被PyObject_SetItem/PyObject_DelItem使用(在mp_ass_subscript之后);NULL表示不支持。sq_contains(objobjproc):可被PySequence_Contains使用;NULL时该函数退化为线性遍历直到找到匹配。sq_inplace_concat/sq_inplace_repeat:被PySequence_InPlaceConcat/PySequence_InPlaceRepeat使用,应就地修改第一个操作数并返回它;NULL时分别回退到PySequence_Concat/PySequence_Repeat;也用于增广赋值+=/*=(在对应nb_inplace_*之后)。
缓冲协议:PyBufferProcs
PyBufferProcs 实现缓冲协议,定义 exporter 如何向 consumer 暴露内部数据:
bf_getbuffer(int (PyObject *exporter, Py_buffer *view, int flags)):处理 exporter 的导出请求,除第 (3) 步外必须执行:(1) 判断能否满足请求,不能则抛 BufferError、置 view->obj = NULL 并返回 -1;(2) 填写请求的字段;(3) 递增内部导出计数;(4) 置 view->obj 为 exporter 并 INCREF;(5) 返回 0。
free-threaded 构建下的线程安全要求:导出计数递增必须原子;底层缓冲数据必须在所有导出的生命周期内有效且地址稳定;支持扩容/重分配的对象(如 bytearray)在执行此类操作前必须原子检查导出计数,有导出时抛 BufferError;函数需支持多线程并发调用。
缓冲提供者链/树可用两种方案:Re-export(每个节点把自己作为 exporter,view->obj 指向自己的新引用)或 Redirect(请求重定向到树根,view->obj 指向根对象的新引用)。Py_buffer 指向的所有内存属于 exporter,必须在没有 consumer 前保持有效;format、shape、strides、suboffsets、internal 字段对 consumer 只读。PyBuffer_FillInfo 可以简单暴露 bytes 缓冲并正确处理所有请求类型;consumer 侧接口是 PyObject_GetBuffer。
bf_releasebuffer(void (PyObject *exporter, Py_buffer *view)):处理释放请求,无资源可释放时可以为 NULL;标准实现:(1) 递减内部导出计数;(2) 计数归零时释放 view 关联的全部内存。free-threaded 构建下计数递减必须原子,计数归零时的资源清理也必须原子(最后一次释放可能与其他线程的并发释放竞争,释放只能发生一次)。exporter 必须用 Py_buffer.internal 字段跟踪缓冲专属资源(该字段保证恒定,而 consumer 可能传入 view 的副本)。该函数不得 DECREF view->obj——由 PyBuffer_Release 自动完成(该设计有利于打破引用环)。
异步协议:PyAsyncMethods(3.5 引入)
typedef struct {
unaryfunc am_await;
unaryfunc am_aiter;
unaryfunc am_anext;
sendfunc am_send;
} PyAsyncMethods;
am_await:PyObject *am_await(PyObject *self),返回值必须是迭代器(PyIter_Check返回 1);非 awaitable 可置NULL。am_aiter:PyObject *am_aiter(PyObject *self),必须返回异步迭代器对象;未实现异步迭代协议可置NULL。am_anext:PyObject *am_anext(PyObject *self),必须返回 awaitable 对象;可置NULL。am_send(3.10 引入):PySendResult am_send(PyObject *self, PyObject *arg, PyObject **result),见PyIter_Send;可置NULL。
完整示例:从零定义一个静态类型
以下示例完整继承自原文档 Examples 一节。
最基本的静态类型:
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 = PyDoc_STR("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 */
PyDoc_STR("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 */
};
支持弱引用、实例字典与哈希的类型(注意 Py_TPFLAGS_MANAGED_DICT / Py_TPFLAGS_MANAGED_WEAKREF 搭配 Py_TPFLAGS_HAVE_GC 使用):
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 = PyDoc_STR("My objects"),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT |
Py_TPFLAGS_MANAGED_WEAKREF,
.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 的子类、不可再被继承、且不能通过调用创建实例(用 Py_TPFLAGS_DISALLOW_INSTANTIATION,实例由独立工厂函数创建;tp_base 因 C 初始化限制在模块 init 中赋 &PyUnicode_Type):
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 = PyDoc_STR("my custom str"),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
.tp_repr = (reprfunc)myobj_repr,
};
最简单的定长与变长静态类型(变长示例中 tp_basicsize 减去末尾可变数组占位、tp_itemsize 为单元素大小,这是 PyObject_VAR_HEAD + 尾部数组布局的标准写法):
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 *),
};
延伸阅读
- 类型结构的字段详解与本文同源:Doc/c-api/typeobj.rst;
- 结构体定义:Include/cpython/object.h、Doc/includes/typestruct.h;
- 继承/默认值填充的实现:Objects/typeobject.c(
type_ready)与 Objects/typeobject.c(PyType_Ready); - 文档在 Examples 一节同时指引读者参考 "defining new types" 教程与 "new types" 专题,获取更系统的扩展类型开发实践(见 Doc/c-api/typeobj.rst 末尾)。
编写自定义类型的总体工作流可以概括为:定义实例布局(PyObject_HEAD/PyObject_VAR_HEAD 开头)→ 声明 PyTypeObject 并填 tp_name/tp_basicsize 等必填项 → 按需实现 tp_new/tp_init/tp_dealloc/tp_free 与协议槽位 → 在模块初始化函数中修正 ob_type、tp_base 等无法静态初始化的字段 → 调用 PyType_Ready(静态类型)或 PyType_FromSpec 家族(堆类型)完成继承槽位拷贝、MRO 计算与默认值填充。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00