str=\"python语言程序设计\",python中str函数可以实现
本文主要介绍Python内置str的源代码学习。有需要的朋友可以借鉴一下,希望能有所帮助。祝大家进步很大,早日升职加薪。
00-1010简介1 Unicode 2.1 Unicode 2.1 Unicode对象在Python中的好处2.2 Python对Unicode的优化3 Unicode对象的底层结构3.1 PyasciiObject 3.2 PyCompactionCodeObject 3.3 PyunicodeObject 3.4示例4 interned机制5摘要
目录
“深度理解Python内置类型”的内容将从源码的角度向大家介绍Python中各种常用的内置类型。
在介绍常见类型str之前,在上一篇博客:Python源码学习笔记:深入了解Python内置类型——bytes中,我们已经介绍了与str密切相关的字节的源码知识。本博客回顾了str相关源代码的分析。
引言
计算机存储的基本单位是字节,由8位组成。由于英文只是由26个字母加上一些符号组成,所以英文字符可以直接以字节保存。但是,其他语言(如中国、日本、韩国等。)由于字符量大,不得不用多个字节编码。
随着计算机技术的普及,非拉丁字符编码技术得到了发展,但仍然存在两大局限性:
不支持多语言:一种语言的编码方案不能在另一种语言中使用,没有统一的标准:比如中文有很多编码标准,如GBK、GB2312、GB18030等。由于编码方式不统一,开发者需要在不同的代码之间来回转换,不可避免的会出现很多错误。为了解决这种不一致性,人们提出了Unicode标准。Unicode对世界上大多数文本系统进行排列和编码,以便计算机能够以统一的方式处理文本。目前Unicode已经收集了超过14万个字符,自然支持多种语言。(Unicode的uni是“统一”的根源)
1 Unicode
2 Python中的Unicode
Python 3以后,str对象在内部用Unicode表示,所以在源代码中变成了Unicode对象。使用Unicode的好处是程序的核心逻辑统一使用Unicode,在输入和输出层面只需要解码和编码,可以最大程度的避免各种编码问题。
图示如下:
2.1 Unicode对象的好处
问题:由于Unicode有14万多个字符,每个字符至少需要4个字节才能保存(这里应该是因为2个字节不够,所以只用4个字节,一般不用3个字节)。而英文字符用ASCII码表示只需要1个字节,常用英文字符用Unicode的话成本会是原来的4倍。
首先,我们来看看Python中不同形式的str对象的大小差异:
sys . get sizeof( ab )-sys . get sizeof( a )
一个
sys . get sizeof( one two )-sys . get sizeof( one )
2
sys . get sizeof( )-sys . get sizeof( )
四
所以Python对Unicode对象进行了优化:根据文本内容,选择底层存储单元。
根据文本字符的Unicode码位范围,Unicode对象的基础存储分为三类:
PyUnicode_1BYTE_KIND:所有字符的码位都在U 0000和U 00FF之间。PyUnicode_2BYTE_KIND:所有字符的码位都在U000和U FFFF之间,至少有一个字符的码位大于U00FFPyUnicode _ 1byte _ k
IND:所有字符码位在U+0000到U+10FFFF之间,且至少有一个字符的码位大于U+FFFF
对应枚举如下:
enum PyUnicode_Kind {/* String contains only wstr byte characters. This is only possible
when the string was created with a legacy API and _PyUnicode_Ready()
has not been called yet. */
PyUnicode_WCHAR_KIND = 0,
/* Return values of the PyUnicode_KIND() macro: */
PyUnicode_1BYTE_KIND = 1,
PyUnicode_2BYTE_KIND = 2,
PyUnicode_4BYTE_KIND = 4
};
根据不同的分类,选择不同的存储单元:
/* Py_UCS4 and Py_UCS2 are typedefs for the respectiveunicode representations. */
typedef uint32_t Py_UCS4;
typedef uint16_t Py_UCS2;
typedef uint8_t Py_UCS1;
对应关系如下:
由于Unicode内部存储结构因文本类型而异,因此类型kind必须作为Unicode对象公共字段进行保存。Python内部定义了一些标志位,作为Unicode公共字段:(介于笔者水平有限,这里的字段在后续内容中不会全部介绍,大家后续可以自行了解。抱拳~)
- interned:是否为interned机制维护
- kind:类型,用于区分字符底层存储单元大小
- compact:内存分配方式,对象与文本缓冲区是否分离
- asscii:文本是否均为纯ASCII
通过PyUnicode_New函数,根据文本字符数size以及最大字符maxchar初始化Unicode对象。该函数主要是根据maxchar为Unicode对象选择最紧凑的字符存储单元以及底层结构体:(源码比较长,这里就不列出了,大家可以自行了解,下面以表格形式展现)
maxchar < 128 128 <= maxchar < 256 256 <= maxchar < 65536 65536 <= maxchar < MAX_UNICODE
3 Unicode对象的底层结构体
3.1 PyASCIIObject
C源码:
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;
源码分析:
length:文本长度
hash:文本哈希值
state:Unicode对象标志位
wstr:缓存C字符串的一个wchar_t指针,以\0结束(这里和我看的另一篇文章讲得不太一样,另一个描述是:ASCII文本紧接着位于PyASCIIObject结构体后面,我个人觉得现在的这种说法比较准确,毕竟源码结构体后面没有别的字段了)
图示如下:
(注意这里state字段后面有一个4字节大小的空洞,这是结构体字段内存对齐造成的现象,主要是为了优化内存访问效率)
ASCII文本由wstr指向,以’abc’和空字符串对象’'为例:
3.2 PyCompactUnicodeObject
如果文本不全是ASCII,Unicode对象底层便由PyCompactUnicodeObject结构体保存。C源码如下:
/* Non-ASCII strings allocated through PyUnicode_New use thePyCompactUnicodeObject structure. state.compact is set, and the data
immediately follow the structure. */
typedef struct {
PyASCIIObject _base;
Py_ssize_t utf8_length; /* Number of bytes in utf8, excluding the
* terminating \0. */
char *utf8; /* UTF-8 representation (null-terminated) */
Py_ssize_t wstr_length; /* Number of code points in wstr, possible
* surrogates count as two code points. */
} PyCompactUnicodeObject;
PyCompactUnicodeObject在PyASCIIObject的基础上增加了3个字段:
utf8_length:文本UTF8编码长度
utf8:文本UTF8编码形式,缓存以避免重复编码运算
wstr_length:wstr的长度(这里所谓的长度没有找到很准确的说法,笔者也不太清楚怎么能打印出来,大家可以自行研究下)
注意到,PyASCIIObject中并没有保存UTF8编码形式,这是因为ASCII本身就是合法的UTF8,这也是ASCII文本底层由PyASCIIObject保存的原因。
结构图示:
3.3 PyUnicodeObject
PyUnicodeObject则是Python中str对象的具体实现。C源码如下:
/* Strings allocated through PyUnicode_FromUnicode(NULL, len) use thePyUnicodeObject structure. The actual string data is initially in the wstr
block, and copied into the data block using _PyUnicode_Ready. */
typedef struct {
PyCompactUnicodeObject _base;
union {
void *any;
Py_UCS1 *latin1;
Py_UCS2 *ucs2;
Py_UCS4 *ucs4;
} data; /* Canonical, smallest-form Unicode buffer */
} PyUnicodeObject;
3.4 示例
在日常开发时,要结合实际情况注意字符串拼接前后的内存大小差别:
>>> import sys>>> text = a * 1000
>>> sys.getsizeof(text)
1049
>>> text +=
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。