Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 增强提案

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_refcnto->ob_refcnt 不可能引用相同的内存,因此函数应该返回 0,而根本无需在 return 语句中获取 ob_refcnt 的值。对于 GCC,Python 现在使用 -fno-strict-aliasing 来解决此问题;对于其他编译器,它可能只是看到未定义的行为。即使使用 GCC,使用 -fno-strict-aliasing 也可能不必要地使生成的代码性能下降。

规范

标准 C 对其别名规则有一个特定的例外,专门设计用于支持 Python 的情况:结构体类型的变量也可以通过指向其第一个字段的指针进行访问。例如,如果一个结构体以一个 int 开头,则 struct * 也可以转换为 int *,允许将 int 值写入第一个字段。

对于 Python,PyObject_HEADPyObject_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 的定义是规范性的,因此扩展作者可以选择使用宏,或者将其 ob_base 字段显式地放入其结构体中。

按照惯例,基字段应该称为 ob_base。但是,所有对 ob_refcnt 和 ob_type 的访问必须将对象指针强制转换为 PyObject*(除非指针已知具有该类型),并且应该使用相应的访问器宏。为了简化对 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_INCREFPy_DECREF 将被更改为将其参数强制转换为 PyObject *,以便模块作者也可以在为 Python 2.6 设计的模块中显式声明 ob_base 字段。


来源:https://github.com/python/peps/blob/main/peps/pep-3123.rst

上次修改:2023-09-09 17:39:29 GMT