我一直在研究PEP 393之后Python如何表示字符串,但我不了解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?字符不足以表示ASCII字符串吗?另外,如果PyASCIIObject已经具有wchar_t指针,为什么PyCompactUnicodeObject也具有char指针?我的理解是两个指针都指向同一位置,但是为什么要同时包含两个指针呢?
PEP 373确实是您问题的最佳参考,尽管有时也需要C-API文档。让我们一一解答您的问题:
您拥有正确的类型。但是有一个不明显的折痕:当您使用两种“紧凑”类型(PyASCIIObject
或PyCompactUnicodeObject
)时,结构本身只是一个标题。字符串的实际数据在结构之后立即存储在内存中。数据使用的编码由该kind
字段描述,并将取决于字符串中的最大字符值。
前两个结构中的wstr
和utf8
指针是C代码请求一个可以存储转换表示形式的位置。对于ASCII字符串(使用PyASCIIObject
),UTF-8数据不需要缓存指针,因为ASCII数据本身是UTF-8兼容的。宽字符缓存仅由不推荐使用的功能使用。
这两个缓存指针永远不会指向同一位置,因为它们的类型不是直接兼容的。对于紧凑型字符串,仅在需要UTF-8缓冲区(例如PyUnicode_AsUTF8AndSize
)或Py_UNICODE
缓冲区(例如不推荐使用PyUnicode_AS_UNICODE
)的函数被调用时才分配它们。
对于使用不推荐使用的Py_UNICODE
基于API的字符串创建的字符串,wstr
指针还有额外的用途。它指向字符串数据的唯一版本,直到在字符串PyUnicode_READY
上调用宏为止。第一次准备字符串时,data
将使用Latin-1,UTF-16和UTF-32中可能的最紧凑的编码,创建一个新的缓冲区,并将字符存储在其中。该wstr
缓冲区将被保留,因为以后其他想要查找PY_UNICODE
字符串的API函数可能会需要使用该缓冲区。
有趣的是,您现在正在询问CPython的内部字符串表示形式,因为目前正在进行有关是否wchar *
可以在即将发布的Python版本中删除不推荐使用的字符串API函数和实现细节(如指针)的讨论。尽管计划可能会在此之前发生变化,尤其是如果对野外使用的代码的影响比预期严重的话,看起来Python 3.11.0(可能会在2022年发布)可能会发生这种情况。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句