为什么Python有相同的不可变对象id不同?
4 个回答
<Python源码剖析>上解释的很清楚:
Python里一切都是对象.
所以1,2,3,4...这些整数也都是对象.这些基本的不可变对象在python里会被频繁的引用,创建,如果不找到好的办法的话很容易让python引发效率瓶颈,所以python引入了整数对象池的机制.
intobject.c中代码片段
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they
can be shared.
The integers that are saved are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif
上面清楚写了[-5, 256) 这些小整数被定义在了这个对象池里.所以当引用小整数时会自动引用整数对象池里的对象的.
至于字符串对象也是不可变对象,python有个intern机制,简单说就是维护一个字典,这个字典维护已经创建字符串(key)和它的字符串对象的地址(value),每次创建字符串对象都会和这个字典比较,没有就创建,重复了就用指针进行引用就可以了.
float类型可以认为每个赋值都是创建一个对象,因为float有点多,所以没必要和int一样了.
至于tuple它是不可变对象,理应和int和string一样会做一个缓存,但是书上没有说明,于是看了看源码,发现tuple的数据结构很简单,简单到不能再简单,就是一个数组,里面是元组的迭代对象,这个对象指向的是各个元素.最关键的是元组没有实现intern机制!所以元组虽然是不可变对象,但它同时也是一个数组,这个数组和c里的数组一样,每次创建都会分配内存空间,so~~
smallint,具体来说[-5, 256) 这个范围内的int,Python是用一个smallint数组来维护的,相当于一个解释器level的缓存。而超出这个范围的int,则会在每次声明的时候分别创建。float和tuple同理。至于最后那个string的例子,这个'aaaaa'应该是被作为常量interned了,s和r实际上都指向这个被interned的对象,所以id自然就相同。