定位槽位¶
若要通过 C API 定义 module objects 和 classes,您可以使用一个 slots 数组——本质上,这些是用于描述待创建对象特性的键值对。这种方式将数据与运行时使用的结构解耦,使得 CPython 及其他 Python C API 实现能够在不破坏向后兼容性的前提下更新这些结构。
本节是对槽位的总体介绍。 对于对象专属行为和具体槽位值,请参阅应用了槽位的函数的文档:
用于类型的
PyType_FromSlots();用于模块的
PyModule_FromSlotsAndSpec()和 扩展导出钩子。
当槽被传递给一个用于应用这些槽的函数时,该函数不会修改槽数组,也不会修改其指向的任何数据(递归地)。函数执行完毕后,调用者可以修改该数组及其指向的任何数据(递归地),但明确标记为 PySlot_STATIC 的数据除外。
除非另有记录说明,否则同一 ID(sl_id)的多个槽位不会出现在同一个槽位数组中。
在 3.15 版本加入: Slot 数组推广了早期定义对象的一种方式:使用 PyType_Spec 与 PyType_Slot 表示类型,使用 PyModuleDef 与 PyModuleDef_Slot 表示模块。早期的 API 是 软弃用;目前没有计划将其移除。
slot 数组的条目采用以下结构:
-
type PySlot¶
- 属于 稳定 ABI (包括所有成员) 自 3.15 版起.
槽位数组中的一个条目。定义如下:
typedef struct { uint16_t sl_id; uint16_t sl_flags; union { uint32_t sl_reserved; // must be 0 }; union { void *sl_ptr; void (*sl_func)(void); Py_ssize_t sl_size; int64_t sl_int64; uint64_t sl_uint64; }; } PySlot;
-
uint16_t sl_id¶
槽位 ID,选自:
值为零的
sl_id(Py_slot_end)标志着槽位数组的结束。
-
void *sl_ptr¶
-
void (*sl_func)(void)¶
-
Py_ssize_t sl_size¶
-
int64_t sl_int64¶
-
uint64_t sl_uint64¶
槽位的数据。这些成员属于一个匿名联合体;具体使用哪个成员取决于槽位 ID 所要求的数据类型:分别为数据指针、函数指针、大小、有符号整数或无符号整数。
除非针对特定槽位 ID 另有说明,否则指针(即
sl_ptr和sl_func)不得为 NULL。
-
uint16_t sl_flags¶
以下标志中的零个或多个标志,以 OR 逻辑组合而成:
-
PySlot_STATIC¶
- 属于 稳定 ABI 自 3.15 版起.
slot 所指向的所有数据都是静态分配的且为常量。因此,解释器无需复制这些信息。
该标志对于函数指针而言是隐含的。
该标志同样适用于槽位“间接”指向的数据,但通过
Py_slot_subslots嵌套的槽位除外;这类槽位可能拥有自己的PySlot_STATIC标志。例如,若将该标志应用于一个指向PyMemberDef结构体数组的Py_tp_members槽位,则整个数组以及其中各元素的名称和文档字符串都必须是静态且常量的。
-
PySlot_INTPTR¶
- 属于 稳定 ABI 自 3.15 版起.
数据存储在
sl_ptr中;CPython 会将其转换为相应的类型。此标志可简化从较旧的
PyType_Slot和PyModuleDef_Slot结构迁移的过程。
-
PySlot_STATIC¶
在 3.15 版本加入.
-
uint16_t sl_id¶
便捷宏¶
-
PySlot_DATA(name, value)¶
-
PySlot_FUNC(name, value)¶
-
PySlot_SIZE(name, value)¶
-
PySlot_INT64(name, value)¶
-
PySlot_UINT64(name, value)¶
-
PySlot_STATIC_DATA(name, value)¶
- This macro is available when compiling for the 稳定 ABI 自 3.15 版起.
用于通过
sl_id和特定的联合体成员集来定义PySlot结构的便捷宏。PySlot_STATIC_DATA设置PySlot_STATIC标志位;其他项不设置任何标志位。请注意,这些宏使用了*指定初始化子*——这是 C++ 在 2020 年标准版本中引入的一项 C 语言特性。如果您的代码需要与 C++11 或更早版本保持兼容,请使用
PySlot_PTR代替。定义为:
#define PySlot_DATA(NAME, VALUE) \ {.sl_id=NAME, .sl_ptr=(void*)(VALUE)} #define PySlot_FUNC(NAME, VALUE) \ {.sl_id=NAME, .sl_func=(VALUE)} #define PySlot_SIZE(NAME, VALUE) \ {.sl_id=NAME, .sl_size=(VALUE)} #define PySlot_INT64(NAME, VALUE) \ {.sl_id=NAME, .sl_int64=(VALUE)} #define PySlot_UINT64(NAME, VALUE) \ {.sl_id=NAME, .sl_uint64=(VALUE)} #define PySlot_STATIC_DATA(NAME, VALUE) \ {.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)}
在 3.15 版本加入.
-
PySlot_END¶
- This macro is available when compiling for the 稳定 ABI 自 3.15 版起.
用于标记
PySlot数组结束的便利宏。定义为:
#define PySlot_END {0}在 3.15 版本加入.
-
PySlot_PTR(name, value)¶
-
PySlot_PTR_STATIC(name, value)¶
- This macro is available when compiling for the 稳定 ABI 自 3.15 版起.
适用于 C++11 兼容代码的便捷宏。此版本的 C++ 不允许在字面量中设置任意联合成员;相反,这些宏会设置
PySlot_INTPTR标志,并将该值类型转换为(void*)。定义为:
#define PySlot_PTR(NAME, VALUE) \ {NAME, PySlot_INTPTR, {0}, {(void*)(VALUE)}} #define PySlot_PTR_STATIC(NAME, VALUE) \ {NAME, PySlot_INTPTR|PySlot_STATIC, {0}, {(void*)(VALUE)}}
在 3.15 版本加入.
常用槽位 ID¶
以下槽位 ID 可用于类型定义和模块定义。
-
Py_slot_subslots¶
- 属于 稳定 ABI 自 3.15 版起.
嵌套槽位数组。
变量 (
sl_ptr) 应指向一个由PySlot结构体组成的数组。该数组中的槽位(最多包含至但不包括零 ID 终止符)将被视为在出现当前槽位数组中Py_slot_subslots的位置时插入的槽位。槽嵌套深度限制为 5 级。此限制未来可能会取消。
在 3.15 版本加入.
-
Py_slot_invalid¶
- 属于 稳定 ABI 自 3.15 版起.
保留;始终将被视为未知槽位 ID。定义为
UINT16_MAX(0xFFFF)。当附带
PySlot_OPTIONAL旗标使用时,将定义一个无效果的槽位。 如无该旗标,则处理带有此 ID 的槽位将会失败。在 3.15 版本加入.