文章目录
Python的对象分为 “可变对象” 和 “不可变对象”, 可变对象也还可以分为 “可变” 和 “不可变”,这里所谓的可变就是说对象所维护的数据是可以变化的,举个例子说明,list容器中的元素可以进行添加、删除、修改等操作,也就是说这个容器对象所维护的数据是可以动态变化的;而所谓 “不可变” 就是说,此对象中所维护的数据一旦创建后就不能发生改变,即便对这个对象进行某种操作后生成的数据也只能是一个新的地址,例如tuple容器。这一节我们将研究Python中的字符串对象。
1、Python2中的PystringObject
在本节我们也会先来解析一下Python2中对于字符串对象的实现,然后再简述一下在Python3中的字符串对象和Python2中有什么不同。实际Python中的字符串对象就是通过PyStringObject来实现的, 它是一个内存大小可变的一个对象(不是说不可变吗?怎么又可变了?什么鬼?)。之所以说它可变是因为在创建字符串对象的时候,我们是不能提前预知字符串的长度的,所以在PyStringObject对象中必须要有可以用来记录字符串长度的成员。举个例子,‘java’ 和 ‘Python’ 这两个字符串的长度显然是不一样的,因此这两个字符串所占用的内存空间也是不一样的。But talk is cheap, show me the code, 让我们来看看底层C语言的实现
// stringobject.h
typedef struct {
PyObject_VAR_HEAD; // 这是在PyObject中定义的宏
long ob_shash;
int ob_sstate;
char ob_sval[1];
}PyStringObject;
可以非常清楚地看见,PyStringObject的头部实际上是一个PyObject_VAR_HEAD,这个头部中维护了一个ob_size的变量,这个变量用来保存可变内存的大小。
ob_shash变量是用来缓存该对象的哈希值,之所以缓存哈希值是因为避免重复计算,它具体的实现算法大家有兴趣可以去参考一下源码,这里我们的重点是解析Python,就不展开了。
ob_sstate变量用于记录该对象是否已经经过了intern机制的处理,这个intern机制是个啥?我们后面会详细聊这个牛逼哄哄的玩意儿。
ob_sval是一个字符数组,这是个啥玩意儿?为啥数组长度只有1 ?你接着往下看就知道了。实际上这货是一个字符指针,这个指针指向了一段内存,而这段内存就是这个字符串对象中所维护的实际的字符串。这段装有实际字符串的字节数(在c语言中一个字符用一个字节来存储)就是由上面说的PyObject_VAR_HEAD中的ob_size变量来维护的。需要注意的是,ob_sval这个字符指针指向的内存字节数也就是长度并不是ob_size,而是ob_size+1。我们知道在C语言中,对于一段字符串结束的标志是一个叫做 ‘\0’ 的字符,所以在PyStringObject的字符串对象中,不以 ‘\0’ 作为结束处理,万一这个字符串中间有这个字符呢,那不就傻X了吗?所以我们在最后末尾添加结束字符,所以这段内存就必须满足ob_sval[ob_size + 1] = ‘\0’. 实际上在Python2中所有变长对象的实现机制都是基于这个叫做ob_size的玩意儿来的。
与Python中的整数对象一样,PyStringObject对象也有多种创建方式。原生的创建方式就是通过PyString_FromString
// stringobject.c
PyObject* PyString_FromString(const char *str) {
register size_t size;
register PyStringObject *op;
// (1)判断字符串长度
size = strlen(str);
if (size > PY_SSIZE_T_MAX) {
return null;
}
// (2)处理null string
if (size == 0 && (op = nullstring) != NULL) {
return (PyObject *)op;
}
// (3)处理字符
if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
return (PyObject *)op;
}
// 创建新的PyStringObject对象
op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size

本文深入解析Python2中的PyStringObject和Python3中的PyUnicodeObject。PyStringObject通过PyObject_VAR_HEAD的ob_size变量记录字符串长度,PyUnicodeObject使用不同的编码方式节省内存。字符串创建涉及intern机制,用于内存优化。Python2的字符串连接效率较低,推荐使用join方法提高性能。
最低0.47元/天 解锁文章
775

被折叠的 条评论
为什么被折叠?



