PEP 3123 – 使 PyObject_HEAD 符合标准 C
- 作者:
- Martin von Löwis <martin at v.loewis.de>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2007年4月27日
- Python 版本:
- 3.0
- 发布历史:
目录
摘要
Python 目前依赖于未定义的 C 行为,因为它使用了 PyObject_HEAD。本 PEP 提议将其更改为符合标准 C。
基本原理
标准 C 定义了对象只能通过其类型的指针访问,所有其他访问都是未定义的行为,除了少数例外。特别是,以下代码具有未定义的行为
struct FooObject{
PyObject_HEAD
int data;
};
PyObject *foo(struct FooObject*f){
return (PyObject*)f;
}
int bar(){
struct FooObject *f = malloc(sizeof(struct FooObject));
struct PyObject *o = foo(f);
f->ob_refcnt = 0;
o->ob_refcnt = 1;
return f->ob_refcnt;
}
这里的问题是存储既被访问为 struct PyObject,又被访问为 struct FooObject。
历史上,编译器对这段代码没有任何问题。然而,现代编译器将该条款用作优化机会,发现 f->ob_refcnt 和 o->ob_refcnt 不可能指向同一内存,因此函数应该返回 0,而无需在 return 语句中获取 ob_refcnt 的值。对于 GCC,Python 现在使用 -fno-strict-aliasing 来解决这个问题;对于其他编译器,它可能只是看到未定义的行为。即使使用 GCC,使用 -fno-strict-aliasing 也可能不必要地恶化生成的代码。
规范
标准 C 对其别名规则有一个特定的例外,正是为了支持 Python 的情况:struct 类型的值也可以通过指向第一个字段的指针访问。例如,如果一个 struct 以一个 int 开头,那么 struct * 也可以被转换为 int *,允许将 int 值写入第一个字段。
对于 Python,PyObject_HEAD 和 PyObject_VAR_HEAD 将被修改为不再列出所有字段,而是列出类型为 PyObject/PyVarObject 的单个字段
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size;
} PyVarObject;
#define PyObject_HEAD PyObject ob_base;
#define PyObject_VAR_HEAD PyVarObject ob_base;
定义为固定大小结构体的类型将把 PyObject 作为其第一个字段,可变大小对象则为 PyVarObject。例如:
typedef struct {
PyObject ob_base;
PyObject *start, *stop, *step;
} PySliceObject;
typedef struct {
PyVarObject ob_base;
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
上述 PyObject_HEAD 的定义是规范性的,因此扩展作者可以(MAY)使用宏,或者将 ob_base 字段显式地放入他们的结构体中。
作为惯例,基字段应(SHOULD)被称为 ob_base。然而,所有对 ob_refcnt 和 ob_type 的访问都必须(MUST)将对象指针转换为 PyObject*(除非已知该指针已经具有该类型),并且应(SHOULD)使用相应的访问器宏。为了简化对 ob_type、ob_refcnt 和 ob_size 的访问,宏
#define Py_TYPE(o) (((PyObject*)(o))->ob_type)
#define Py_REFCNT(o) (((PyObject*)(o))->ob_refcnt)
#define Py_SIZE(o) (((PyVarObject*)(o))->ob_size)
已添加。例如,代码块
#define PyList_CheckExact(op) ((op)->ob_type == &PyList_Type)
return func->ob_type->tp_name;
需要改为
#define PyList_CheckExact(op) (Py_TYPE(op) == &PyList_Type)
return Py_TYPE(func)->tp_name;
对于类型对象的初始化,当前的序列
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
变得不正确,必须替换为
PyVarObject_HEAD_INIT(NULL, 0)
与 Python 2.6 的兼容性
为了支持同时兼容 Python 2.6 和 Python 3.0 的模块,Py_* 宏已添加到 Python 2.6 中。Py_INCREF 和 Py_DECREF 宏将被修改为将其参数转换为 PyObject *,以便模块作者也可以在为 Python 2.6 设计的模块中显式声明 ob_base 字段。
版权
本文档已置于公共领域。
来源: https://github.com/python/peps/blob/main/peps/pep-3123.rst
上次修改: 2025-02-01 08:59:27 GMT