python源码分析笔记(3)

本文主要介绍Python中PyStringObject对象,它是可变长度内存的不变对象,头部有ob_size维护内存大小,ob_sval指向实际字符串。还介绍了其类型对象、创建方式,重点阐述了intern机制,该机制可避免重复创建相同字符串对象,减少内存占用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.字符串对象

PyStringObject是一个拥有可变长度内存的对象,对于表示‘hi’和'Python'的两个不同的PyStringObject对象,其内部所需的保存字符串内容的的内存空间显然是不一样的。同时,PyStringObject对象又是一个不变对象,当创建了一个PyStringObject对象之后,改对象内部维护的字符串就不能改变了。

typedef struct {
    PyObject_VAR_HEAD
    long ob_shash;
    int ob_sstate;
    char ob_sval[1];
}PyStringObject

PyStringObject的头部实际是一个PyObject_VAR_HEAD,其中有一个ob_size变量保存着对象中维护的可变长度内存的大小。虽然在定义中ob_sval是一个字符的字符数组,但是ob_sval实际上是作为一个字符指针指向一段内存的,这段内存保持着这个字符串对象所维护的实际字符串。显然这段内存不会只是一个字节,这段内存的实际长度(字节)正是由ob_size来维护的,这个机制是Python中所有变长对象的实现机制。

同C中的字符串一样,PyStringObject内部维护的字符串在末尾必须以‘\0’结尾,但是由于字符串的实际长度是由ob_size维护的,所以PyStringObject表示的字符串对象中间是有可能出现‘\0'的。所以实际上,ob_sval指向的是一段长度为ob_size+1个字节的内存,而且必须满足ob_sval[ob_size]=='\0'

PyStringObject中的ob_shash变量的作用是缓存该对象的hash值,这样可以避免每一次都重新计算该字符串对象的hash值。如果一个PyStringObeject没有被计算过hash值,那么ob_shash的初始值-1。这个hash在剖析dict的时候会发挥巨大作用。生成hash的代码如下:

static long string_hash(PyStringObject *a)
(
    register int len;
    register unsigned char *p;
    register long x;
    
    if (a->ob_shash != -1 )
        return a->ob_shash;
    len = a->ob_size
    p = (unsigned char *) a->ob_val
    x = *p << 7;
    while (--len >=0)
        x=(1000003*x) ^ *p++;
    x ^= a->ob_size;
    if ( x==-1)
        x= -2
    a->ob_shash = x;
    return a
)

ob_sstate变量标记了该对象是否经过intern机制的处理,关于PyStringObject的Intern机制,后面会详细介绍。

2.PyStringObject 所对应的类型对象 PyString_Type

3.PyStringObject对象的创建

 

4.字符串对象的intern机制

当字符数组的长度为0,或1时,需要进行一个特别的动作:PyString_InternInplace,这就是前面所提到的intern机制。


//intern 长度较短的PyStringObject对象
if (size==0)  (
    PyObject *t = (PyObject *)op
    PyString_InternInplace(&t);
    op = (PyStringObject *)t
    nullstring = op;
) else if (size==-1) (

    PyObject *t  = (PyObject *)op;
    PyString_InternInplace(&t);
    op = (PyStringObject *)t;
    characters[*str & UCHAR_MAX] = op;
)
    return (PyObject *) op;

对于被intern之后的字符串,比如’Ruby',在整个Python运行期间,系统中只有唯一与字符串‘Ruby'对应的PyStringObject对象。

例如 a='Python' , b='Python' ,如果对于a应用了intern机制,那么之后要创建bd的时候,Python会首先在系统中记录的已经被intern机制处理了的PyStringObject对象中查找,如果发现该字符数组对应的PyStringObject对象已经存在,那么就将该对象的引用返回。而不是重新创建一个。下面是 PyString_InternInplace的源码:

首先会进行一系列的检查,检查是否PyStringObject对象,intern只能用于PyStringObject对象,甚至他的派生类对象都不会运用。第二检查是否已经被intern过,Python不会对同一个对象intern两次。

intern机制的核心是interned,interned实际上是一个PyDictObject对象,也就是我们常用的dict。现在一切都清楚了,所谓的intern机制的关键,就是系统中有一个(key,value)映射关系的集合,记录着被intern机制处理过的PyStringObject对象。后来作者又提到,其实PyStringObjectb并不是在创建的时候就通过interned实现了节省空间的目的,无论如何一个合法的PyStringObject对象总会被创建,尽管interned中已经有一个与之对应的PyStringObject对象了,而python在运行时创建了一个PyStringObject对象temp之后,基本上都会调用PyString_InternInplace对temp进行处理,interned会减少temp的引用计数,当引用计数为0时,就会被销毁,它只是作为一个临时对象昙花一现的在内存中闪现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值