Python源码剖析(四)字符串对象
bilibili视频讲解:https://space.bilibili.com/431392724
b站用户名:平凡的久月
1. PyBytesObject
变长对象(数据长度在定义时是不知道的,只能在创建时才能确定)
不可变对象(改变值内存地址会发生改变)
1.1 定义
// Include/bytesobject.h
#ifndef Py_LIMITED_API
typedef struct {PyObject_VAR_HEADPy_hash_t ob_shash;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.*/
} PyBytesObject;
#endif// python3 中不再使用 PyBytesObject 作为 String 类的底层实现
#define PyObject_VAR_HEAD PyVarObject ob_base;typedef struct {PyObject ob_base;Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;typedef struct _object {_PyObject_HEAD_EXTRAPy_ssize_t ob_refcnt;struct _typeobject *ob_type;
} PyObject;// 等价于下列表达
typedef struct {Py_hash_t ob_shash; // 缓存该对象的hash值,避免重新计算(初始值-1),dict中详细解释作用// PyBytesObject内部维护的字符串必须以'\0'结尾char ob_sval[1]; // 字符指针,指向ob_size+1个字节的内存'\0'Py_ssize_t ob_size;Py_ssize_t ob_refcnt;struct _typeobject *ob_type;
} PyBytesObject;
#endif
PyTypeObject PyBytes_Type = {PyVarObject_HEAD_INIT(&PyType_Type, 0)"bytes",PyBytesObject_SIZE, // ob_sizesizeof(char), // 一个字节bytes_dealloc, /* tp_dealloc */0, /* tp_print */0, /* tp_getattr */0, /* tp_setattr */0, /* tp_reserved */(reprfunc)bytes_repr, /* tp_repr */// 支持三种操作&bytes_as_number, /* tp_as_number */&bytes_as_sequence, /* tp_as_sequence */&bytes_as_mapping, /* tp_as_mapping */(hashfunc)bytes_hash, /* tp_hash */// ......0, /* tp_init */0, /* tp_alloc */bytes_new, /* tp_new */PyObject_Del, /* tp_free */
};
1.2 创建PyBytesObject
Python提供了多种路径从C中原生的字符串创建PyBytesObject对象
PyBytes_FromString
PyObject * PyBytes_FromString(const char *str) {size_t size;PyBytesObject *op;assert(str != NULL);size = strlen(str);// 判断字符串长度是否超过系统寻址能力if (size > PY_SSIZE_T_MAX - PyBytesObject_SIZE) {PyErr_SetString(PyExc_OverflowError,"byte string is too long");return NULL;}// 处理空串:通过nullstring,始终只有一个if (size == 0 && (op = nullstring) != NULL) {#ifdef COUNT_ALLOCSnull_strings++; #endifPy_INCREF(op);return (PyObject *)op;}// 处理单个字符(字符串对象缓冲池)if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {#ifdef COUNT_ALLOCSone_strings++; #endifPy_INCREF(op);return (PyObject *)op;}// 创建新的PyBytesObject对象,并初始化/* Inline PyObject_NewVar */op = (PyBytesObject *)PyObject_MALLOC(PyBytesObject_SIZE + size);if (op == NULL)return PyErr_NoMemory();(void)PyObject_INIT_VAR(op, &PyBytes_Type, size);op->ob_shash = -1;memcpy(op->ob_sval, str, size+1);/* share short strings */if (size == 0) {nullstring = op;Py_INCREF(op);} else if (size == 1) {characters[*str & UCHAR_MAX] = op;Py_INCREF(op);}return (PyObject *) op; }
python3中没有ob_sstate这个变量!!!
PyBytes_FromStringAndSize
PyObject * PyBytes_FromStringAndSize(const char *str, Py_ssize_t size) {PyBytesObject *op;if (size < 0) {PyErr_SetString(PyExc_SystemError,"Negative size passed to PyBytes_FromStringAndSize");return NULL;}// 单个字符if (size == 1 && str != NULL &&(op = characters[*str & UCHAR_MAX]) != NULL){#ifdef COUNT_ALLOCSone_strings++; #endifPy_INCREF(op);return (PyObject *)op;}// 创建新的PyBytesObject对象,并初始化op = (PyBytesObject *)_PyBytes_FromSize(size, 0);if (op == NULL)return NULL;if (str == NULL)return (PyObject *) op;memcpy(op->ob_sval, str, size);/* share short strings */if (size == 1) {characters[*str & UCHAR_MAX] = op;Py_INCREF(op);}return (PyObject *) op; }
1.3 intern机制
1.3.1 Python2中
Python2中通过PyString_InternInPlace实现intern机制
检查两项内容:
(1)是否是PyBytesObject?
(2)是否被intern机制处理过(保证只处理一次)
void
PyString_InternInPlace(PyObject **p)
{register PyStringObject *s = (PyStringObject *)(*p);PyObject *t;if (s == NULL || !PyString_Check(s))Py_FatalError("PyString_InternInPlace: strings only please!");/* If it's a string subclass, we don't really know what puttingit in the interned dict might do. */if (!PyString_CheckExact(s))return;if (PyString_CHECK_INTERNED(s))return;if (interned == NULL) {interned = PyDict_New();if (interned == NULL) {PyErr_Clear(); /* Don't leave an exception */return;}}t = PyDict_GetItem(interned, (PyObject *)s);if (t) {Py_INCREF(t);Py_SETREF(*p, t);return;}if (PyDict_SetItem(interned, (PyObject *)s, (PyObject *)s) < 0) {PyErr_Clear();return;}/* The two references in interned are not counted by refcnt.The string deallocator will take care of this */Py_REFCNT(s) -= 2;PyString_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAL;
}
intern是什么?PyDictObject对象–interned
创建临时变量a,在intern中寻找是否有一样的对象。
- intern中的指针不作为a的有效引用(不然a永远无法销毁)
细节问题:
(1)为什么创建的时候interned的键与值都设置为对象的PyObject指针?
(2)为什么将对象的引用计数减2?
1.3.2 Python3中
Python3中做了修改,移动到了sys库,编译器默认执行
from six.moves import intern
from sys import intern
str = "shanghai"
print(intern(str).__doc__)
// Modules/pyexpat.c
static PyObject*
string_intern(xmlparseobject *self, const char* str)
{PyObject *result = conv_string_to_unicode(str);PyObject *value;/* result can be NULL if the unicode conversion failed. */if (!result)return result;if (!self->intern)return result;value = PyDict_GetItem(self->intern, result);if (!value) {if (PyDict_SetItem(self->intern, result, result) == 0)return result;elsereturn NULL;}Py_INCREF(value);Py_DECREF(result);return value;
}
1.4 字符串缓冲池
一个字节的字符对应的对象缓冲池
static PyBytesObject *characters[UCHAR_MAX + 1];
static PyBytesObject *nullstring;
实现过程
(1)创建PyBytesObject对象
(2)进行intern操作
(3)缓存进缓冲池
1.5 与效率相关的问题
背景:实现100个字符串的拼接
实现方法:+
问题:创建N-1个对象,进行N-1次内存的申请与释放
根本原因:不可变对象
解决方法:对存储在list的一组对象进行连接操作(join)
一次申请N个对象使用的内存,并统计这些对象维护的字符串有多长,然后申请内存,最后拷贝到内存空间。
a = 345
b = a
c = 456
d = 456
print(a is b)
print(a is c)
print(c is d)e = "abc"
f = "abc"
g = "abd"
print(e is f)
print(e is g)# True
# False
# True
# True
# False
Python源码剖析(四)字符串对象相关推荐
- Python源码剖析[19] —— 执行引擎之一般表达式(2)
Python源码剖析 --Python执行引擎之一般表达式(2) 本文作者: Robert Chen(search.pythoner@gmail.com ) 3.2 Simple.py 前面我 ...
- Python源码剖析2-字符串对象PyStringObject
二. 1.PyStringObject与 PyString_Type PyStringObject是变长对象中的不可变对象.当创建了一个PyStringObject对象之后,该对象内部维护的字符串就不 ...
- python源码剖析—— python中的列表对象
1. PyListObject对象 PyListObject 对象可以有效地支持插入,添加,删除等操作,在 Python 的列表中,无一例外地存放的都是 PyObject 的指针.所以实际上,你可以这 ...
- python源码剖析—— python中的字节码对象初探
一.代码对象 每个初学python的人都会认为python是一种解释型语言,这个不能说错.但是python并不是真的对执行的python代码的每一行进行解释,虽然我们有一个所谓的"解释器&q ...
- python源码剖析笔记1——Python对象初见
本文简书地址:http://www.jianshu.com/p/763f6cec7a9b 工作整两年了,用python最多,然而对于python内部机制不一定都清楚,每天沉醉于增删改查的简单逻辑编写, ...
- 《Python源码剖析》读书笔记
<Python源码剖析>电子书下载 http://download.csdn.net/detail/xiarendeniao/5130403 Python源码在官网有下载链接,用ctags ...
- Python源码剖析[16] —— Pyc文件解析
Python源码剖析[16] -- Pyc文件解析 2008-02-28 18:29:55| 分类: Python |举报 |字号 订阅 Python源码剖析 --Pyc文件解析 本文作者: Rob ...
- Python源码剖析[1] —— 编译Python
[ 绝对原创,转载请注明出处] 注意 :第一部分Python总体架构采用了网络文档<The Architecture of Python>,这是网络上唯一可见的以剖析Python实现为己任 ...
- Python猫荐书系统之四:《Python源码剖析》
大家好,新一期的荐书栏目如期跟大家见面了. 先来看看今天的主角是谁:<Python源码剖析--深度探索动态语言核心技术>,2008年出版,作者 @陈儒 ,评分8.7分. 是的,你没看错,出 ...
- Python源码剖析:前言
第0章:前言 0.0 我的前言 在几个月学习的中,已经学习了python基本.进阶的语法,如果有读者不清楚的话,可以参考我之前的专栏<python进阶>. 而在这个专栏<pyth ...
最新文章
- html 类型转换,JavaScript怎么进行类型转换?
- python环境变量配置_Python的安装、认识、配置环境变量以及helloworld打印的两种方式
- 华为新系统鸿蒙能互通吗,「连接」万物的鸿蒙,能拯救华为手机吗?
- 选择嵌套_如何优雅地在JavaScript中访问嵌套对象
- 安卓手机刷软路由_华为路由AX3 Pro上手测评:用过最方便的路由器,没有之一...
- 数据分析之numpy
- dynamic programming 学习
- Git学习文档之一 学习文档-合并分支
- matlab波形反白,基于MATLAB的海岸污染物浓度扩散实验分析
- emc测试e3软件系数导入,EMC测试标准
- c语言求婚代码大全,求一个C语言表白的代码
- 为什么MASKRCNN中使用ROIAlign替代ROIPool
- DOS定时关机命令 windowXp
- 【数据压缩】使用Audacity软件分析浊音、清音爆破音的时域及频域特性。
- webpack4打包js
- jquery 封装幻灯插件_21个jQuery幻灯片插件
- win7关机一直卡在正在关机
- win10照片查看器_非常好用的19个Win10小技巧,学会之后事半功倍
- Ispell in Emacs
- Netty 数据传输载体 —— ByteBuf
热门文章
- 电路设计中的防爆设计原理与注意事项分析
- 写给非网工的CCNA教程(2)第一个协议--ARP协议
- 04 - 雷达的工作频率
- 高性能mysql第一章——架构
- Linux驱动加载总结
- ztek usb转串口 linux,Z-tek驱动下载_Z-tek usb转串口驱动官方下载 - 系统之家
- 高德地图提示com.autonavi.amap.mapcore.MapCore.nativeNewInstance问题
- 【lizhi125】比Nero更好用的免费小巧的光盘刻录软件——ImgBurn(中文版)
- VS2013过期激活,VS2013激活,vs2013序列号,VS2013密钥,VS013产品密匙
- 抖音计算机音乐的id,抖音卡点音乐叫什么名字 抖音卡点bgm介绍