Python >= 3.3 内部字符串表示
Python >= 3.3 Internal String Representation
我正在研究 Python 在 PEP 393 之后如何表示字符串,但我不理解 PyASCIIObject 和 PyCompactUnicodeObject 之间的区别。
我的理解是字符串用以下结构表示:
typedef struct {
PyObject_HEAD
Py_ssize_t length; /* Number of code points in the string */
Py_hash_t hash; /* Hash value; -1 if not set */
struct {
unsigned int interned:2;
unsigned int kind:3;
unsigned int compact:1;
unsigned int ascii:1;
unsigned int ready:1;
unsigned int :24;
} state;
wchar_t *wstr; /* wchar_t representation (null-terminated) */
} PyASCIIObject;
typedef struct {
PyASCIIObject _base;
Py_ssize_t utf8_length;
char *utf8;
Py_ssize_t wstr_length;
} PyCompactUnicodeObject;
typedef struct {
PyCompactUnicodeObject _base;
union {
void *any;
Py_UCS1 *latin1;
Py_UCS2 *ucs2;
Py_UCS4 *ucs4;
} data;
} PyUnicodeObject;
如果我错了请纠正我,但我的理解是 PyASCIIObject 仅用于具有 ASCII 字符的字符串,PyCompactUnicodeObject 使用 PyASCIIObject 结构并且它用于具有至少一个非 ASCII 字符的字符串,而 PyUnicodeObject 是用于遗留功能。对吗?
此外,为什么 PyASCIIObject 使用 wchar_t?一个 char 不足以表示 ASCII 字符串吗?
另外,如果 PyASCIIObject 已经有一个 wchar_t 指针,为什么 PyCompactUnicodeObject 也有一个 char 指针?我的理解是两个指针都指向同一个位置,但为什么要同时包含两个指针?
PEP 373 is really the best reference for your questions, though the C-API docs 有时也需要。让我们一一解答您的问题:
你选对了类型。但是有一个 non-obvious 问题:当您使用任何一种 "compact" 类型(PyASCIIObject
或 PyCompactUnicodeObject
)时,结构本身只是一个 header.字符串的实际数据存储在内存中的结构之后。数据使用的编码由 kind
字段描述,并将取决于字符串中的最大字符值。
前两个结构中的wstr
和utf8
指针是可以存储转换后表示的地方,如果C代码需要的话。对于 ASCII 字符串(使用 PyASCIIObject
),UTF-8 数据不需要缓存指针,因为 ASCII 数据本身是 UTF-8 兼容的。宽字符缓存仅由已弃用的函数使用。
这两个缓存指针永远不会指向同一个地方,因为它们的类型不直接兼容。对于紧凑字符串,它们仅在调用需要 UTF-8 缓冲区(例如 PyUnicode_AsUTF8AndSize
) or a Py_UNICODE
buffer (e.g. the deprecated PyUnicode_AS_UNICODE
)的函数时分配。
对于使用已弃用的基于 Py_UNICODE
的 API 创建的字符串,wstr
指针有额外的用途。它指向字符串数据的 only 版本,直到对字符串调用 PyUnicode_READY
宏。第一次准备好字符串时,将创建一个新的 data
缓冲区,并使用 Latin-1、UTF-16 和 UTF-32 中最紧凑的编码将字符存储在其中。 wstr
缓冲区将被保留,因为稍后其他想要查找 PY_UNICODE
字符串的已弃用 API 函数可能需要它。
有趣的是,您现在正在询问 CPython 的内部字符串表示,因为有一个讨论 currently ongoing 关于是否已弃用的字符串 API 函数和实现细节例如 wchar *
指针可以在即将推出的 Python 版本中删除。看起来 Python 3.11.0(预计将于 2022 年发布)可能会发生这种情况,但在此之前计划仍可能会发生变化,特别是如果对在野使用的代码的影响比预期的更严重.
我正在研究 Python 在 PEP 393 之后如何表示字符串,但我不理解 PyASCIIObject 和 PyCompactUnicodeObject 之间的区别。
我的理解是字符串用以下结构表示:
typedef struct {
PyObject_HEAD
Py_ssize_t length; /* Number of code points in the string */
Py_hash_t hash; /* Hash value; -1 if not set */
struct {
unsigned int interned:2;
unsigned int kind:3;
unsigned int compact:1;
unsigned int ascii:1;
unsigned int ready:1;
unsigned int :24;
} state;
wchar_t *wstr; /* wchar_t representation (null-terminated) */
} PyASCIIObject;
typedef struct {
PyASCIIObject _base;
Py_ssize_t utf8_length;
char *utf8;
Py_ssize_t wstr_length;
} PyCompactUnicodeObject;
typedef struct {
PyCompactUnicodeObject _base;
union {
void *any;
Py_UCS1 *latin1;
Py_UCS2 *ucs2;
Py_UCS4 *ucs4;
} data;
} PyUnicodeObject;
如果我错了请纠正我,但我的理解是 PyASCIIObject 仅用于具有 ASCII 字符的字符串,PyCompactUnicodeObject 使用 PyASCIIObject 结构并且它用于具有至少一个非 ASCII 字符的字符串,而 PyUnicodeObject 是用于遗留功能。对吗?
此外,为什么 PyASCIIObject 使用 wchar_t?一个 char 不足以表示 ASCII 字符串吗? 另外,如果 PyASCIIObject 已经有一个 wchar_t 指针,为什么 PyCompactUnicodeObject 也有一个 char 指针?我的理解是两个指针都指向同一个位置,但为什么要同时包含两个指针?
PEP 373 is really the best reference for your questions, though the C-API docs 有时也需要。让我们一一解答您的问题:
你选对了类型。但是有一个 non-obvious 问题:当您使用任何一种 "compact" 类型(
PyASCIIObject
或PyCompactUnicodeObject
)时,结构本身只是一个 header.字符串的实际数据存储在内存中的结构之后。数据使用的编码由kind
字段描述,并将取决于字符串中的最大字符值。前两个结构中的
wstr
和utf8
指针是可以存储转换后表示的地方,如果C代码需要的话。对于 ASCII 字符串(使用PyASCIIObject
),UTF-8 数据不需要缓存指针,因为 ASCII 数据本身是 UTF-8 兼容的。宽字符缓存仅由已弃用的函数使用。这两个缓存指针永远不会指向同一个地方,因为它们的类型不直接兼容。对于紧凑字符串,它们仅在调用需要 UTF-8 缓冲区(例如
PyUnicode_AsUTF8AndSize
) or aPy_UNICODE
buffer (e.g. the deprecatedPyUnicode_AS_UNICODE
)的函数时分配。对于使用已弃用的基于
Py_UNICODE
的 API 创建的字符串,wstr
指针有额外的用途。它指向字符串数据的 only 版本,直到对字符串调用PyUnicode_READY
宏。第一次准备好字符串时,将创建一个新的data
缓冲区,并使用 Latin-1、UTF-16 和 UTF-32 中最紧凑的编码将字符存储在其中。wstr
缓冲区将被保留,因为稍后其他想要查找PY_UNICODE
字符串的已弃用 API 函数可能需要它。
有趣的是,您现在正在询问 CPython 的内部字符串表示,因为有一个讨论 currently ongoing 关于是否已弃用的字符串 API 函数和实现细节例如 wchar *
指针可以在即将推出的 Python 版本中删除。看起来 Python 3.11.0(预计将于 2022 年发布)可能会发生这种情况,但在此之前计划仍可能会发生变化,特别是如果对在野使用的代码的影响比预期的更严重.