Python 源码剖析(三)【字符串对象】
三、字符串对象
1、PyStringObject与PyString_Type
2、创建PyStringObject对象
3、Intern 机制
4、字符缓冲池
5、PyStringObject 效率相关问题
6、Hack PyStringObject
1、PyStringObject与PyString_Type
PyStringObject 对象的声明:
[stringobject.h] typedef struct { PyObject_VAR_HEAD long ob_shash; int ob_sstate; char ob_sval[]; } PyStringObject;
PyObject_VAR_HEAD 中含有字符串长度ob_size,ob_sval指向字符串存储内存,大小为ob_size+1,且ob_sval[ob_size]='\0'; ob_shash保存字符串哈希值,没计算过的默认为0,计算方法为:
[stringobject.c] static long string_hash(PyStringObject *a) { register int len; register unsigned char *p; register long x; if (a->ob_shash != -) return a->ob_shash; len = a->ob_size; p = (unsigned char *) a->ob_sval; x = *p << ; while (--len >= ) x = (*x) ^ *p++; x ^= a->ob_size; if (x == -) x = -; a->ob_shash = x; return x; }
ob_sstate表示该对象是否被Intern。
PyStringObject对应的类型对象:
[stringobject.c] PyTypeObject PyString_Type = { PyObject_HEAD_INIT(&PyType_Type) , "str", sizeof(PyStringObject), sizeof(char), …… (reprfunc)string_repr, /* tp_repr */ &string_as_number, /* tp_as_number */ &string_as_sequence, /* tp_as_sequence */ &string_as_mapping, /* tp_as_mapping */ (hashfunc)string_hash, /* tp_hash */ , /* tp_call */ …… string_new, /* tp_new */ PyObject_Del, /* tp_free */ };
2、创建PyStringObject对象
最一般的方法PyString_FromString:
[stringobject.c] PyObject * PyString_FromString(const char *str) { register size_t size; register PyStringObject *op; assert(str != NULL); /*判断字符串长度*/ size = strlen(str); if (size > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "string is too long for a Python string"); return NULL; } /*处理null string*/ if (size == && (op = nullstring) != NULL) { #ifdef COUNT_ALLOCS null_strings++; #endif Py_INCREF(op); return (PyObject *)op; } if (size == && (op = characters[*str & UCHAR_MAX]) != NULL) { #ifdef COUNT_ALLOCS one_strings++; #endif Py_INCREF(op); return (PyObject *)op; } /* 创建新的PyStringObject对象,并初始化 */ /* Inline PyObject_NewVar */ op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); op->ob_shash = -; op->ob_sstate = SSTATE_NOT_INTERNED; memcpy(op->ob_sval, str, size+); /* Itern(共享)长度较短的PyStringObject对象 */ if (size == ) { PyObject *t = (PyObject *)op; PyString_InternInPlace(&t); op = (PyStringObject *)t; nullstring = op; Py_INCREF(op); } else if (size == ) { PyObject *t = (PyObject *)op; PyString_InternInPlace(&t); op = (PyStringObject *)t; characters[*str & UCHAR_MAX] = op; Py_INCREF(op); } return (PyObject *) op; }
首先判断有没有超过长度限制(INT_MAX 2147483647 ),然后判断是不是为空串,如果是并且之前创建过空串对象就直接返回,接着就判断其是否为一个字符,是而且在Intern机制中创建过就直接返回,否则,就开始申请内存(PyStringObject + size),创建对象并返回。
另一个创建PyStringObject对象的途径:
[stringobject.c] PyObject* PyString_FromStringAndSize(const char *str, int size) { register PyStringObject *op; /*处理null string*/ if (size == && (op = nullstring) != NULL) { #ifdef COUNT_ALLOCS null_strings++; #endif Py_INCREF(op); return (PyObject *)op; } if (size == && str != NULL && (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(sizeof(PyStringObject) + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); op->ob_shash = -; op->ob_sstate = SSTATE_NOT_INTERNED; if (str != NULL) memcpy(op->ob_sval, str, size); op->ob_sval[size] = '\0'; /* share short strings */ if (size == ) { PyObject *t = (PyObject *)op; PyString_InternInPlace(&t); op = (PyStringObject *)t; nullstring = op; Py_INCREF(op); } else if (size == && str != NULL) { PyObject *t = (PyObject *)op; PyString_InternInPlace(&t); op = (PyStringObject *)t; characters[*str & UCHAR_MAX] = op; Py_INCREF(op); } return (PyObject *) op; }
和前一个差不多。
3、Intern 机制
现在看下前面提到的Intern机制,即创建PyStringObject对象函数里的PyString_InternInPlace:
[stringobjec.c] 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 putting it 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_DECREF(*p); *p = t; return; } if (PyDict_SetItem(interned, (PyObject *)s, (PyObject *)s) < ) { PyErr_Clear(); return; } /* The two references in interned are not counted by refcnt. The string deallocator will take care of this */ s->ob_refcnt -= ; PyString_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAL; }
首先进行一系列检测,而核心在于 interned(PyDictObject对象),如果创建对象在interned中,增加interned中对象的引用计数,减少传入对象的引用计数(其为临时变量);如果创建对象不在interned中,将创建对象加入interned,并将其引用计数减2(interned中对象的引用应当无效,否则无法删除)。对象引用计数为0时会被销毁:
[stringobject.c] static void string_dealloc(PyObject *op) { switch (PyString_CHECK_INTERNED(op)) { case SSTATE_NOT_INTERNED: break; case SSTATE_INTERNED_MORTAL: /* revive dead object temporarily for DelItem */ op->ob_refcnt = ; if (PyDict_DelItem(interned, op) != ) Py_FatalError( "deletion of interned string failed"); break; case SSTATE_INTERNED_IMMORTAL: Py_FatalError("Immortal interned string died."); default: Py_FatalError("Inconsistent interned string state."); } op->ob_type->tp_free(op); }
4、字符缓冲池
PyStringObject 为一个字节的字符对象设计了一个类似PyIntObject小整数对象池一样的对象池characters:
static PyStringObject *characters[UCHAR_MAX + 1];
#define UCHAR_MAX 0xff /* maximum unsigned char value */
由字符串创建的代码可看出characters缓冲池在字符串生成时才开始生成。
虽然在创建PyStringObject时Intern机制只作用于 空串和字符(对应nullstring和characters),但Intern机制会作用到其他地方。
5、PyStringObject 效率相关问题
字符串连接问题,用 '+' 效率低下,连接N个PyStringObject对象将进行N-1次内存申请及搬运,推荐使用join(只分配一次内存)。
'+' 调用string_concat:
[stringobject.c] static PyObject* string_concat(register PyStringObject *a, register PyObject *bb) { register unsigned int size; register PyStringObject *op; #define b ((PyStringObject *)bb) …… size = a->ob_size + b->ob_size; /* Inline PyObject_NewVar */ op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); if (op == NULL) return PyErr_NoMemory(); PyObject_INIT_VAR(op, &PyString_Type, size); op->ob_shash = -; op->ob_sstate = SSTATE_NOT_INTERNED; memcpy(op->ob_sval, a->ob_sval, (int) a->ob_size); memcpy(op->ob_sval + a->ob_size, b->ob_sval, (int) b->ob_size); op->ob_sval[size] = '\0'; return (PyObject *) op; #undef b }
而join则这样(先计算处list或tuple中PyStringObject的总大小再申请一次内存):
[stringobject.c] static PyObject* string_join(PyStringObject *self, PyObject *orig) { char *sep = PyString_AS_STRING(self); const int seplen = PyString_GET_SIZE(self); PyObject *res = NULL; char *p; int seqlen = ; size_t sz = ; int i; PyObject *seq, *item; 。。。。。。//获得list中PyStringObject对象的个数,保存在seqlen中 for (i = ; i < seqlen; i++) { const size_t old_sz = sz; item = PySequence_Fast_GET_ITEM(seq, i); sz += PyString_GET_SIZE(item); if (i != ) sz += seplen; } /* 申请内存空间 */ res = PyString_FromStringAndSize((char*)NULL, (int)sz); /* 连接list中的每一个PyStringObject对象*/ p = PyString_AS_STRING(res); for (i = ; i < seqlen; ++i) { size_t n; /* 获得list中的一个PyStringObject对象*/ item = PySequence_Fast_GET_ITEM(seq, i); n = PyString_GET_SIZE(item); memcpy(p, PyString_AS_STRING(item), n); p += n; if (i < seqlen - ) { memcpy(p, sep, seplen); p += seplen; } } Py_DECREF(seq); return res; }
6、Hack PyStringObject
可在string_length中添加打印地址代码,运行len()时可观察到相同的字符串地址一样,这就是Intern机制的作用;可在len()中添加代码:
static void ShowCharater() { char a = 'a'; PyStringObject** posA = characters+(unsigned short)a; int i; for(i = ; i < ; ++i) { PyStringObject* strObj = posA[i]; printf("%s, %d\n", strObj->ob_sval, strObj->ob_refcnt); } }
观察是否使用缓冲池对象。
Python 源码剖析(三)【字符串对象】的更多相关文章
- 《Python 源码剖析》之对象
py一切皆对象的实现 Python中对象分为两类: 定长(int等), 非定长(list/dict等) 所有对象都有一些相同的东西, 源码中定义为PyObject和PyVarObject, 两个定义都 ...
- Python源码剖析——01内建对象
<Python源码剖析>笔记 第一章:对象初识 对象是Python中的核心概念,面向对象中的"类"和"对象"在Python中的概念都为对象,具体分为 ...
- Python 源码剖析(一)【python对象】
处于研究python内存释放问题,在阅读部分python源码,顺便记录下所得.(基于<python源码剖析>(v2.4.1)与 python源码(v2.7.6)) 先列下总结: ...
- Python开发【源码剖析】 List对象
前言 本文探讨的Python版本为2.7.16,可从官网上下载,把压缩包Python-2.7.16.tgz解压到本地即可 需要基本C语言的知识(要看的懂) PyListObject对象 PyListO ...
- Python源码剖析——02虚拟机
<Python源码剖析>笔记 第七章:编译结果 1.大概过程 运行一个Python程序会经历以下几个步骤: 由解释器对源文件(.py)进行编译,得到字节码(.pyc文件) 然后由虚拟机按照 ...
- python源码剖析学习记录-01
学习<Python源码剖析-深度探索动态语言核心技术>教程 Python总体架构,运行流程 File Group: 1.Core Modules 内部模块,例如:imp ...
- Python源码剖析|百度网盘免费下载|Python新手入门|Python新手学习资料
百度网盘免费下载:Python源码剖析|新手免费领取下载 提取码:g78z 目录 · · · · · · 第0章 Python源码剖析——编译Python0.1 Python总体架构0.2 Pyth ...
- Python 源码剖析 目录
Python 源码剖析 作者: 陈儒 阅读者:春生 版本:python2.5 版本 本博客园的博客记录我会适当改成Python3版本 阅读 Python 源码剖析 对读者知识储备 1.C语言基础知识, ...
- Python 源码剖析(六)【内存管理机制】
六.内存管理机制 1.内存管理架构 2.小块空间的内存池 3.循环引用的垃圾收集 4.python中的垃圾收集 1.内存管理架构 Python内存管理机制有两套实现,由编译符号PYMALLOC_DEB ...
- Django Rest Framework源码剖析(三)-----频率控制
一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...
随机推荐
- 全国Uber优步司机奖励政策 (1月11日-1月17日)
本周已经公开奖励整的城市有:北 京.成 都.重 庆.上 海.深 圳.长 沙.佛 山.广 州.苏 州.杭 州.南 京.宁 波.青 岛.天 津.西 安.武 汉.厦 门,可按CTRL+F,搜城市名快速查找. ...
- P1208 [USACO1.3]混合牛奶 Mixing Milk
P1208 [USACO1.3]混合牛奶 Mixing Milk 题目描述 由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要.帮助Marry乳业找到最优的牛奶采购方案. Marry乳业 ...
- 在线大数据cdh迁移,从公有云迁移到私有云
1.添加新节点 2.迁移hdfs和hbase数据: 3.迁移源节点各个服务到新节点: 4.利用HA,包括hdfs,hbase master,resourcemanager的ha实现在线迁移: 5.数据 ...
- 机器学习常用算法(LDA,CNN,LR)原理简述
1.LDA LDA是一种三层贝叶斯模型,三层分别为:文档层.主题层和词层.该模型基于如下假设:1)整个文档集合中存在k个互相独立的主题:2)每一个主题是词上的多项分布:3)每一个文档由k个主题随机混合 ...
- Servlet处理文件下载的编码问题,乱码。
Servlet处理文件下载的编码问题,乱码. //处理文件名乱码问题 // 获得请求头中的User-Agent String agent = request.getHeader("User- ...
- 基于Kafka的服务端用户行为日志采集
本文来自网易云社区 作者:李勇 背景 随着互联网的不断发展,用户所产生的行为数据被越来越多的网站重视,那么什么是用户行为呢?所谓的用户行为主要由五种元素组成:时间.地点.人物.行为.行为对应的内容.为 ...
- Python拼接字符串的7种方法
1.直接通过+操作: s = 'Python'+','+'你好'+'!'print(s) 打印结果: Python,你好! 2.通过join()方法拼接: 将列表转换成字符串 strlist=['Py ...
- Python 多线程、进程、协程上手体验
浅谈 Python 多线程.进程.协程上手体验 前言:浅谈 Python 很多人都认为 Python 的多线程是垃圾(GIL 说这锅甩不掉啊~):本章节主要给你体验下 Python 的两个库 Thre ...
- 【label】标签组件说明
label标签组件 用来改进表单组件的可用性,使用for属性找到对应的id,或者将控件放在该标签下,当点击时,就会触发对应的控件.目前可以绑定的控件有:<button/>, <che ...
- CSP201503-2:数字排序
引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的"计算机职业资格认证"考试, ...