定位槽位

若要通过 C API 定义 module objects 和 classes,您可以使用一个 slots 数组——本质上,这些是用于描述待创建对象特性的键值对。这种方式将数据与运行时使用的结构解耦,使得 CPython 及其他 Python C API 实现能够在不破坏向后兼容性的前提下更新这些结构。

本节是对槽位的总体介绍。 对于对象专属行为和具体槽位值,请参阅应用了槽位的函数的文档:

当槽被传递给一个用于应用这些槽的函数时,该函数不会修改槽数组,也不会修改其指向的任何数据(递归地)。函数执行完毕后,调用者可以修改该数组及其指向的任何数据(递归地),但明确标记为 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_OPTIONAL
属于 稳定 ABI 自 3.15 版起.

如果槽位 ID 未知,解释器应忽略该槽位,而非导致失败。

例如,如果 Python 3.16 添加了一项具有新槽位 ID 的新功能,则相应的槽位可能会被标记为 PySlot_OPTIONAL,从而使 Python 3.15 忽略该槽位。

请注意,“可选性”仅适用于未知的槽位 ID。该标志并不会使 Python 跳过已知槽位的无效值。

在 3.15 版本加入.

便捷宏

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_end
属于 稳定 ABI 自 3.15 版起.

标记槽位数组的末尾。定义为 0。

在 3.15 版本加入.

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 版本加入.