python字符串代码对象_Python源码剖析 - Python中的字符串对象
1. 前言
我们已经在 【Python中的整数对象】 章节中对定长对象进行了详细的讲解,接下来我们将介绍变长对象,而字符串类型,则是这类对象的典型代表。
这里必须先引入一个概念:
Python 中的变长对象分为两类:
变长可变对象 - 例如 List,创建后还能添加、删除元素
变长不可变对象 - 例如 String,Tuple, 创建后,不再支持添加、删除等操作
2. PyStringObject初识
PyStringObject 是对字符串对象的实现方式。首先它是一个可变长度的对象,这个可变是只指在创建字符串对象的时候,这个长度并不固定。但是一旦创建完毕后,这个长度就固定了,不能再发生变化。
举例来说:
test_str = "Hello World"
test_url = "https://www.xtuz.net"
显而易见,test_str 的长度和 test_url 的长度并不一样,这个原因就是在字符串对象创建时 PyStringObject 并不限定长度,然后创建完毕后,改对象内部维护的字符串对象就不在改变了。
我们从源码中也可以进行佐证:
typedef struct {
PyObject_VAR_HEAD
long ob_shash;
int ob_sstate;
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
* ob_sstate != 0 iff the string object is in stringobject.c's
* 'interned' dictionary; in this case the two references
* from 'interned' to this object are *not counted* in ob_refcnt.
*/
} PyStringObject;
PyObject_VAR_HEAD 中的 ob_size (详见Python源码剖析 - 对象初探),记录变长对象的内存大小,ov_sval 作为字符指针指向一段内存,这段内存就是实际字符串。比例 test_str 的 ob_size 则为11。
ob_shash 则是该对象的哈希值,这在 dict 类型中是非常有用的,作为 key 值存在。
ob_sstate 则是表明该对象是否经过 intern 机制处理,简单来说就是即值同样的字符串对象仅仅会保存一份,放在一个字符串储蓄池中,是共用的,当然,肯定不能改变,这也决定了字符串必须是不可变对象。
3. PyStringObject创建
从代码上来看,可以有多种创建 PyStringObject 的方式:
PyAPI_FUNC(PyObject *) PyString_FromStringAndSize(const char *, Py_ssize_t);
PyAPI_FUNC(PyObject *) PyString_FromString(const char *);
PyAPI_FUNC(PyObject *) PyString_FromFormatV(const char*, va_list)
Py_GCC_ATTRIBUTE((format(printf, 1, 0)));
PyAPI_FUNC(PyObject *) PyString_FromFormat(const char*, ...)
Py_GCC_ATTRIBUTE((format(printf, 1, 2)));
其中,最常用的则是 PyString_FromString(const char *);
代码实现如下:
PyObject *
PyString_FromString(const char *str)
{
register size_t size;
register PyStringObject *op;
assert(str != NULL);
size = strlen(str);
if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) {
PyErr_SetString(PyExc_OverflowError,
"string is too long for a Python string");
return NULL;
}
if (size == 0 && (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS
null_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}
if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
#ifdef COUNT_ALLOCS
one_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}
/* Inline PyObject_NewVar */
op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
if (op == NULL)
return PyErr_NoMemory();
(void)PyObject_INIT_VAR(op, &PyString_Type, size);
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
Py_MEMCPY(op->ob_sval, str, size+1);
/* share short strings */
if (size == 0) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
nullstring = op;
Py_INCREF(op);
} else if (size == 1) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
return (PyObject *) op;
}
简单来说,主要是三个逻辑:
判断字符串是否过长,过长,则返回 null 指针
判断是否是空串,空串,则则将引用
分配内存,并将字符串复制到 op->ob_sval 中
在完成创建后,内存布局如上所示
4. 字符缓冲池
我们已经在【Python中的整数对象】 中阐述了 Python 对小整数的优化处理,而字符串的intern机制与此类似,其实就是会为长度为1的的字符创建对象池。
if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
#ifdef COUNT_ALLOCS
one_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}
/* share short strings */
if (size == 0) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
nullstring = op;
Py_INCREF(op);
} else if (size == 1) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
每当创建长度为1的字符串的时候,都会把它存到 characters 里面,这样之后创建长度为1的字符时,如果检测到已经在characters里面了,就直接返回这个缓冲的对象,不用进行malloc,这也就是该缓冲池的作用。
5. 字符串对象的intern机制
在 CPython 中字符串的实现原理使用了一种叫做 Intern(字符串驻留)的技术来提高字符串效率。
先来看一段代码:
a='www.xtuz.net'
b='www.xtuz.net'
print(id(a), id(b))
print(a is b)
可以看到如下输出结果
(4420449312, 4420449312)
True
a 和 b 虽然值是一样的,但确实是两个不同的字符串对象,假设程序中存在大量值相同的字符串,系统就不得不为每个字符串重复地分配内存空间,显然,对系统来说是一种无谓的资源浪费。为了解决这种问题,Python 引入了 intern 机制。
Intern 是 Python 中的一个内建函数,该函数的作用就是对字符串进行 intern 机制处理,处理后返回字符串对象。我们发现但凡是值相同的字符串经过 intern 机制处理之后,返回的都是同一个字符串对象,这种方式在处理大数据的时候无疑能节省更多的内存空间,系统无需为相同的字符串重复分配内存,对于值相同的字符串共用一个对象即可。
Intern 实现 机制的方式非常简单,就是通过维护一个字符串储蓄池,这个池子是一个字典结构,如果字符串已经存在于池子中了就不再去创建新的字符串,直接返回之前创建好的字符串对象,如果之前还没有加入到该池子中,则先构造一个字符串对象,并把这个对象加入到池子中去,方便下一次获取。
6. 更多内容
如果你对Python语言感兴趣,可以关注我,或者关注我的微信公众号:xtuz666
python字符串代码对象_Python源码剖析 - Python中的字符串对象相关推荐
- python源码剖析代码例子_Python源码剖析笔记5-模块机制
python中经常用到模块,比如import xxx,from xxx import yyy这样子,里面的机制也是需要好好探究一下的,这次主要从黑盒角度来探测模块机制,源码分析点到为止,详尽的源码分析 ...
- python程序代码解析_Python源码分析3 – 词法分析器PyTokenizer
Introduction 上次我们分析了Python中执行程序可分为5个步骤: Tokenizer进行词法分析,把源程序分解为Token Parser根据Token创建CST CST被转换为AST A ...
- python源码剖析—— python中的列表对象
1. PyListObject对象 PyListObject 对象可以有效地支持插入,添加,删除等操作,在 Python 的列表中,无一例外地存放的都是 PyObject 的指针.所以实际上,你可以这 ...
- python源码剖析—— python中的字节码对象初探
一.代码对象 每个初学python的人都会认为python是一种解释型语言,这个不能说错.但是python并不是真的对执行的python代码的每一行进行解释,虽然我们有一个所谓的"解释器&q ...
- python简单代码加法-CPython 源码中整数加法的实现
最近突然涌起兴趣去阅读 CPython 源码,网上也看了不少解析的文章,后来网上看到<Python源码剖析>评价不错,可惜现在已经绝版,只能从豆瓣阅读购买了一本电子书观摩 . 我从网上下载 ...
- Swoft 源码剖析 - Swoft 中的注解机制
作者:bromine 链接:https://www.jianshu.com/p/ef7... 來源:简书 著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版. Swoft Github ...
- python源码剖析_Python源码剖析 - 对象初探
01 前言 对象是 python 中最核心的一个概念,在python的世界中,一切都是对象,整数.字符串.甚至类型.整数类型.字符串类型,都是对象. 02 什么是PyObject Python 中凡事 ...
- python源码深度剖析_Python源码剖析——深度探索动态语言核心技术 | 学步园
8.3 Python虚拟机的运行框架 当Python启动后,首先会进行Python运行时环境的 初始化.注意这里的运行时环境是一个与上一节剖析的执行环境不同的概念.运行时环境是一个全局的概念,而执行 ...
- python整型图_python源码研究之整型对象探索
1.python的整型对象是PyIntObject对象,这个对象是一个不可变对象,即没有ob_size这个变量,这个对象在c层面实现,只是在基本的pyobject中添加了long ob_ival对 ...
最新文章
- Android 进阶自定义View(4)图表统计LineChartView曲线图的实现
- python3.8安装tensorflow1.14时候报错Can‘t connect to HTTPS URL because the SSL module is not available
- (0064)iOS开发之枚举NS_ENUM和NS_OPTIONS的区别
- Tomcat 架构探索
- ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 80 Days(双向队列+尺取法)
- 用定时中断来接收红外遥控信号
- Struts1.2+Spring2.5+Hibernate3.2框架搭建(十五)
- Objective-C基础语法高速入门
- 地铁上怎么那么多钢管女郎?
- eclipse目录发布到tomcat对应的目录
- 如何搭建自己的第四方支付平台?
- Windows修改键盘映射
- VS+QT多语言实现(中文乱码问题、tr()包含不生成问题)
- qq文件服务器怎么用,怎样使用qq文件助手功能图文教程
- 关于bios+mbr还原uefi+gpt的系统后,出现进不去系统的问题
- 项目需求的重要性和开发步骤
- Eviews中实现ARIMA模型并进行预测
- ubuntu22.04 安装ROS2
- Python爬虫爬取古诗文网站项目分享
- 聊一下最近的裁员大潮
热门文章
- 21世纪商业运作平台:云计算(Cloud Computing)
- 代码结构checklist
- CSS3制作动画的三个属性
- 协程入门(一):启动与挂起
- 场景切换时清理缓存!!CCTextureCache、CCSpriteFrameCache、CCAnimationCache
- 编译生成的Apk安装到手机提示应用未安装,需给apk签名
- springcloud工作笔记094---springcloud项目后端验证_hibernate validator后端校验字段_介绍了解
- C#.Net工作笔记018---葡萄城控件FlexGrid自定义单元格_以及给自定义控件添加自定义事件
- SpringCloud工作笔记053---SLF4J简介与使用(整合log4j_并切换logging)
- 微信公众开放平台开发06---复习一下servlet