为什么Python有相同的不可变对象id不同?

测试环境Python 2.7.3 --- 小一点int类型相等 s = 1 r = 1 # id(s) == id(r) 大一点的int类型就不一样了…
关注者
26
被浏览
10,053

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自然就相同。