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

Posted

技术标签:

【中文标题】Python 3.x 中字符串的内部表示是啥【英文标题】:What is internal representation of string in Python 3.xPython 3.x 中字符串的内部表示是什么 【发布时间】:2010-12-22 17:31:15 【问题描述】:

在 Python 3.x 中,字符串由 Unicode 序数项组成。 (请参阅下面语言参考中的引文。)Unicode 字符串的内部表示是什么?是 UTF-16 吗?

字符串对象的项目是 Unicode 代码单元。一个 Unicode 代码 单位由字符串对象表示 一件物品,可以容纳一个 表示一个 16 位或 32 位值 Unicode 序数(最大值 序数在 sys.maxunicode,取决于如何 Python 在编译时配置)。 代理对可能存在于 Unicode 对象,并将被报告 作为两个单独的项目。

【问题讨论】:

【参考方案1】:

python内部编码中的每个字符都被编码为4个字节。

>>> import array; s = 'Привет мир!'; b = array.array('u', s).tobytes(); print(b); print(len(s) * 4 == len(b))
b'\x1f\x04\x00\x00@\x04\x00\x008\x04\x00\x002\x04\x00\x005\x04\x00\x00B\x04\x00\x00 \x00\x00\x00<\x04\x00\x008\x04\x00\x00@\x04\x00\x00!\x00\x00\x00'
True
>>> import array; s = 'test'; b = array.array('u', s).tobytes(); print(b); print(len(s) * 4 == len(b))
b't\x00\x00\x00e\x00\x00\x00s\x00\x00\x00t\x00\x00\x00'
True
>>> 

【讨论】:

你能详细说明这段代码的作用吗?尤其是不是英文的部分Привет мир! 那篇俄语文章用于描述当您使用 ASCII 以外的字符时会发生什么。顺便说一句,它是“你好世界!”俄语。【参考方案2】:

内部表示从 latin-1、UCS-2 到 UCS-4 不等 . UCS 表示表示长度为 2 或 4 个字节,并且 unicode 代码单元在数字上等于相应的代码点。我们可以通过查找代码单元大小的变化来检查这一点。

显示它们的范围从 latin-1 的 1 个字节到 UCS-4 的 4 个字节:

>>> getsizeof('')           
49
>>> getsizeof('a')  #------------------ + 1 byte as the representaion here is latin-1 
50
>>> getsizeof('\U0010ffff') 
80
>>> getsizeof('\U0010ffff\U0010ffff') # + 4 bytes as the representation here is UCS-4
84

我们可以检查开始的表示确实是 latin-1 而不是 UTF-8,因为对 2 字节代码单元的更改发生在字节边界而不是 ''\U0000007f' - '\U00000080' 边界,如 UTF- 8:

>>> getsizeof('\U0000007f')  
50
>>> getsizeof('\U00000080') #----------The size of the string changes at \x74 - \x80 boundary but..
74
>>> getsizeof('\U00000080\U00000080') # ..the size of the code-unit is still one. so not UTF-8
75

>>> getsizeof('\U000000ff')  
74
>>> getsizeof('\U000000ff\U000000ff')# (+1 byte)    
75
>>> getsizeof('\U00000100')  
76
>>> getsizeof('\U00000100\U00000100') # Size change at byte boundary(+2 bytes). Rep is UCS-2.             
78
>>> getsizeof('\U0000ffff') 
76
>>> getsizeof('\U0000ffff\U0000ffff') # (+ 2 bytes)
78
>>> getsizeof('\U00010000')            
80
>>> getsizeof('\U00010000\U00010000') # (+ 4 bytes) Thes size of the code unit changes to 4 at byte boundary again.
84

【讨论】:

【参考方案3】:

在 Python 3.3 及更高版本中,字符串的内部表示将取决于字符串,可以是 latin-1、UCS-2 或 UCS-4 中的任何一种,如 PEP 393 中所述。

对于以前的 Python,内部表示取决于 Python 的构建标志。可以使用标志值 --enable-unicode=ucs2--enable-unicode=ucs4 构建 Python。 ucs2 构建实际上是 use UTF-16 as their internal representation,而 ucs4 构建使用 UCS-4 / UTF-32。

【讨论】:

这是正确答案,应该被接受。【参考方案4】:

Include/unicodeobject.h 中查看 CPython 3.1.5 的源代码:

/* --- Unicode Type ------------------------------------------------------- */

typedef struct 
    PyObject_HEAD
    Py_ssize_t length;          /* Length of raw Unicode data in buffer */
    Py_UNICODE *str;            /* Raw Unicode buffer */
    long hash;                  /* Hash value; -1 if not set */
    int state;                  /* != 0 if interned. In this case the two
                                 * references from the dictionary to this object
                                 * are *not* counted in ob_refcnt. */
    PyObject *defenc;           /* (Default) Encoded version as Python
                                   string, or NULL; this is used for
                                   implementing the buffer protocol */
 PyUnicodeObject;

字符存储为Py_UNICODE 的数组。在大多数平台上,我相信Py_UNICODE#defined 和wchar_t

【讨论】:

【参考方案5】:

在实现PEP 393 的 Python 3.3 中,内部表示将发生变化。新的表示会选择ascii, latin-1, utf-8, utf-16, utf-32中的一个或几个,一般都是为了得到一个紧凑的表示。

只有在与遗留 API 对话时才会隐式转换为代理对(这些 API 仅存在于 windows 上,其中 wchar_t 是两个字节); Python 字符串将被保留。这是release notes。

【讨论】:

在我看来,PEP 393 表示内部表示是 ASCII、Latin-1 (UCS1)、UCS2 或 UCS4 的最紧凑(给定特定字符串)。即:特别不是 utf-8/16/32。原因:Python 必须是常数时间才能索引到字符串,因此字符必须是统一的大小,UCS 就是这种情况,但 utf 表示不是。 Latin-1 是 ASCII 的超集,因此没有理由将 ASCII 作为选项之一。选项是 (a) 统一 8 位,即 Latin-1,(b) 统一 16 位,即 UCS2,或 (c) 统一 32 位,即 UCS4(与 UTF-32 相同)。值得注意的是 UTF-8 和 UTF-16,它们的每个代码点的位数不统一【参考方案6】:

Python 2.X 和 3.X 之间的 Unicode 内部表示没有变化。

绝对不是 UTF-16。 UTF-anything 是一种面向字节的 EXTERNAL 表示。

已为每个代码单元(字符、代理项等)分配了一个范围为 (0, 2 ** 21) 的数字。这被称为它的“序数”。

真的,您引用的文档说明了一切。大多数 Python 二进制文件使用 16 位序数,这将您限制在基本多语言平面(“BMP”),除非您想使用代理(如果您找不到您的头发衬衫并且您的指甲床已经关闭,这很方便)生锈)。为了使用完整的 Unicode 曲目,您更喜欢“宽版本”(32 位宽)。

简而言之,unicode 对象的内部表示是一个 16 位无符号整数数组,或一个 32 位无符号整数数组(仅使用 21 位)。

【讨论】:

“以 16 位整数存储 unicode 代码点”称为“UCS-2”。用 32 位整数做同样的事情是 UCS-4。 @John:我不明白你想说什么。如果它不是“存储 Unicode 代码点的方式”(或更普遍的字符信息),那么什么是编码。是的,我很清楚 UTF-16 是 UCS-2 的现代替代品,但它与您所说的不同。 UTF-16 支持所有的 Unicode,但 UCS-2 只支持 BMP。 “没有变化……”。实际上,请参阅 PEP 393(2010 年 1 月),其中阐明了随后发生的变化,以及此处的另一个答案“内部表示将改变......” 这个答案是错误的,见下面东武的答案。 PEP 393 是在给出这个答案后一个月写的。 “UTF-anything 是一种面向字节的外部表示”。在某些系统中,它也是有效的内部表示。例如,在许多基于 C++ 的系统中,UTF-8 在内部使用,而不仅仅是用于 I/O。 Go 专门使用 UTF-8 作为内部字符串表示。【参考方案7】:

我认为,UTF-16(只是 16 位字的序列)与 Python 的字符串对象之间的区别很难判断。

如果 python 编译时使用 Unicode=UCS4 选项,它将在 UTF-32 和 Python 字符串之间进行比较。

因此,最好考虑一下,它们属于不同的类别,尽管您可以相互转换。

【讨论】:

【参考方案8】:

这取决于:请参阅here。就内部表示而言,这对于 Python 3 仍然适用。

【讨论】:

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

python默认的字符编码是啥

Python >= 3.3 内部字符串表示

头歌Python中显示回车是啥意思啊?

unicode 在 Python 内部是如何表示的?

python里的end是啥意思

Python字符串是啥,如何使用?