三、字符串对象

1、PyStringObjectPyString_Type

2、创建PyStringObject对象

3、Intern 机制

4、字符缓冲池

5、PyStringObject 效率相关问题

6、Hack PyStringObject


1、PyStringObjectPyString_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 源码剖析(三)【字符串对象】的更多相关文章

  1. 《Python 源码剖析》之对象

    py一切皆对象的实现 Python中对象分为两类: 定长(int等), 非定长(list/dict等) 所有对象都有一些相同的东西, 源码中定义为PyObject和PyVarObject, 两个定义都 ...

  2. Python源码剖析——01内建对象

    <Python源码剖析>笔记 第一章:对象初识 对象是Python中的核心概念,面向对象中的"类"和"对象"在Python中的概念都为对象,具体分为 ...

  3. Python 源码剖析(一)【python对象】

    处于研究python内存释放问题,在阅读部分python源码,顺便记录下所得.(基于<python源码剖析>(v2.4.1)与 python源码(v2.7.6)) 先列下总结:      ...

  4. Python开发【源码剖析】 List对象

    前言 本文探讨的Python版本为2.7.16,可从官网上下载,把压缩包Python-2.7.16.tgz解压到本地即可 需要基本C语言的知识(要看的懂) PyListObject对象 PyListO ...

  5. Python源码剖析——02虚拟机

    <Python源码剖析>笔记 第七章:编译结果 1.大概过程 运行一个Python程序会经历以下几个步骤: 由解释器对源文件(.py)进行编译,得到字节码(.pyc文件) 然后由虚拟机按照 ...

  6. python源码剖析学习记录-01

    学习<Python源码剖析-深度探索动态语言核心技术>教程         Python总体架构,运行流程   File Group: 1.Core Modules 内部模块,例如:imp ...

  7. Python源码剖析|百度网盘免费下载|Python新手入门|Python新手学习资料

    百度网盘免费下载:Python源码剖析|新手免费领取下载 提取码:g78z 目录  · · · · · · 第0章 Python源码剖析——编译Python0.1 Python总体架构0.2 Pyth ...

  8. Python 源码剖析 目录

    Python 源码剖析 作者: 陈儒 阅读者:春生 版本:python2.5 版本 本博客园的博客记录我会适当改成Python3版本 阅读 Python 源码剖析 对读者知识储备 1.C语言基础知识, ...

  9. Python 源码剖析(六)【内存管理机制】

    六.内存管理机制 1.内存管理架构 2.小块空间的内存池 3.循环引用的垃圾收集 4.python中的垃圾收集 1.内存管理架构 Python内存管理机制有两套实现,由编译符号PYMALLOC_DEB ...

  10. Django Rest Framework源码剖析(三)-----频率控制

    一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...

随机推荐

  1. 全国Uber优步司机奖励政策 (1月18日-1月24日)

    本周已经公开奖励整的城市有:北 京.成 都.重 庆.上 海.深 圳.长 沙.佛 山.广 州.苏 州.杭 州.南 京.宁 波.青 岛.天 津.西 安.武 汉.厦 门,可按CTRL+F,搜城市名快速查找. ...

  2. Java String源码解析

    public final class String implements java.io.Serializable, Comparable<String>, CharSequence { ...

  3. CF 314 E. Sereja and Squares

    E. Sereja and Squares http://codeforces.com/contest/314/problem/E 题意: 给你一个擦去了部分左括号和全部右括号的括号序列,括号有25种 ...

  4. LeetCode:34. Search for a Range(Medium)

    1. 原题链接 https://leetcode.com/problems/search-for-a-range/description/ 2. 题目要求 给定一个按升序排列的整型数组nums[ ]和 ...

  5. 一分钟了解spark的调优

    Tuning Spark 数据序列化 内存调优 内存管理概述 确定内存消耗 调整数据结构 序列化 RDD 存储 垃圾收集调整 其他注意事项 并行度水平 减少任务的内存使用 广播大的变量 数据本地化 概 ...

  6. Kubernetes网络方案的三大类别和六个场景

    欢迎访问网易云社区,了解更多网易技术产品运营经验. 本文章根据网易云资深解决方案架构师 王必成在云原生用户大会上的分享整理. 今天我将分享个人对于网络方案的理解,以及网易云在交付 Kubernetes ...

  7. Qt-QML-Repeater-导航条

    上篇文章中,我写了一个自己的Button,也就是美化了一下QML自带的Button 就是上面的这个,剩下的就是放三张图片在上面就可以了,当然了,这个Button在后期,还是会加入更让多的美化,比如,可 ...

  8. MySQL☞upper函数

    upper(列名/字符串):把小写字母改为大写字母 格式: select  upper(列名/字符串) from 表名 如下图:

  9. Java enum类型笔记

    用途: 定义命令行参数,菜单选项,星期,方向(东西南北)等 与普通类的不同 有默认的方法 value() 每个enum类都已默认继承java.lang.Enum,所以enum类不能继承其他类 构造方法 ...

  10. HDU - 6438(贪心+思维)

    链接:HDU - 6438 题意:给出 n ,表示 n 天.给出 n 个数,a[i] 表示第 i 天,物品的价格是多少.每天可以选择买一个物品,或者卖一个已有物品,也可以什么都不做,问最后最大能赚多少 ...