Python >= 3.3 内部字符串表示

Posted

技术标签:

【中文标题】Python >= 3.3 内部字符串表示【英文标题】:Python >= 3.3 Internal String Representation 【发布时间】:2020-10-05 15:42:02 【问题描述】:

我正在研究 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? char 还不足以表示 ASCII 字符串吗? 另外,如果 PyASCIIObject 已经有一个 wchar_t 指针,为什么 PyCompactUnicodeObject 也有一个 char 指针?我的理解是两个指针都指向同一个位置,但是为什么要同时包含两者呢?

【问题讨论】:

【参考方案1】:

PEP 373 确实是您问题的最佳参考,尽管有时也需要the C-API docs。让我们一一解答您的问题:

    你有正确的类型。但是有一个不明显的问题:当您使用任何一种“紧凑”类型(PyASCIIObjectPyCompactUnicodeObject)时,结构本身只是一个标题。字符串的实际数据在结构之后立即存储在内存中。数据使用的编码由kind 字段描述,并将取决于字符串中的最大字符值。

    前两个结构中的wstrutf8 指针是在C 代码请求时可以存储转换后的表示的地方。对于 ASCII 字符串(使用 PyASCIIObject),UTF-8 数据不需要缓存指针,因为 ASCII 数据本身与 UTF-8 兼容。宽字符缓存仅由不推荐使用的函数使用。

    这两个缓存指针永远不会指向同一个地方,因为它们的类型不直接兼容。对于紧凑字符串,它们仅在需要 UTF-8 缓冲区(例如 PyUnicode_AsUTF8AndSize)或 Py_UNICODE 缓冲区(例如已弃用的 PyUnicode_AS_UNICODE)的函数被调用时分配。

    对于使用已弃用的基于 Py_UNICODE 的 API 创建的字符串,wstr 指针有额外的用途。它指向字符串数据的only 版本,直到在字符串上调用PyUnicode_READY 宏。第一次准备好字符串时,将创建一个新的data 缓冲区,并将字符存储在其中,使用Latin-1、UTF-16 和UTF-32 中可能最紧凑的编码。 wstr 缓冲区将被保留,因为稍后其他想要查找 PY_UNICODE 字符串的已弃用 API 函数可能需要它。

有趣的是,您现在正在询问 CPython 的内部字符串表示,因为有一个讨论 currently ongoing 关于是否可以在即将发布的 Python 版本中删除已弃用的字符串 API 函数和实现细节(如 wchar * 指针) .看起来 Python 3.11.0(预计将于 2022 年发布)可能会发生这种情况,但在此之前计划仍可能发生变化,特别是如果对野外使用的代码的影响比预期的更严重的话。

【讨论】:

以上是关于Python >= 3.3 内部字符串表示的主要内容,如果未能解决你的问题,请参考以下文章

Python 3.3 中的哈希函数在会话之间返回不同的结果

[GO_ON]2017-1-18 python基础

【python】求两个字符串的公共字串?

python入坑

06-字符串类型*****

Python 3.x 中字符串的内部表示是啥