1. Python的对象模型
我们知道,在Python的世界里,万物皆对象(Object)。根据Python官方文档对Data Model的说明,每个Python对象均拥有3个特性:身份、类型和值。
官方文档关于对象模型的这段概括说明对于我们理解Python对象是如此重要,所以本文将其摘录如下(为了使得结构更清晰,这里把原文档做了分段处理):
1) Every object has an identity, a type and a value.
2) An object‘s identity never changes once it has been created; you may think of it as the object‘s address in memory. The ‘is‘ operator compares the identity
of two objects; the id() function returns an integer representing its identity (currently implemented as its address).
3) An object‘s type is also unchangeable. An object‘s type determines the operations that the object supports (e.g., "does it have a length?") and also
defines the possible values for objects of that type. The type() function returns an object‘s type (which is an object itself).
4) The value of some objects can change. Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created
are called immutable. (The value of an immutable container object that contains a reference to a mutable object can change when the latter‘s value is changed; however the container is
still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.)
5) An object‘s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.
总结一下:
1) 每个Python对象均有3个特性:身份、类型和值
2) 对象一旦创建,其身份(可以理解为对象的内存地址)就是不可变的。可以借助Python的built-in函数id()来获取对象的id值,可以借助is操作符来比较两个对象是否是同一个对象
3) 已创建对象的类型不可更改,对象的类型决定了可以作用于该对象上的操作,也决定了该对象可能支持的值
4) 某些对象(如list/dict)的value可以修改,这类对象被称为mutable object;而另一些对象(如numbers/strings/tuples)一旦创建,其value就不可修改,故被称为immutable object
5) 对象的值是否可以被修改是由其type决定的
>>> x = 2.11 >>> id(x) 7223328 >>> x += 0.5 >>> x 2.61 >>> id(x) 7223376上述代码中,x += 0.5看起来像是修改了名为x的对象的值。
3. 一个“古怪”的case
按照上述说明,下面的case如何理解呢?
>>> a = 20 >>> b = 20 >>> id(a) 7151888 >>> id(b) 7151888 >>> a is b True上述代码中,a和b应该是不同的对象的引用,它们的id值不相等才对。
#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可见,解释器实现int型对象时,确实申请了一个small_ints数组用于缓存小整数,从宏定义及注释可以看到,缓存的整数范围是[-5, 257)。
至此,我们大概清楚了CPython解释器实现int型对象的细节行为,也知道了我们遇到的那个古怪case的原因。
在交互模式下,我们已经看到CPython解释器确实会缓存小整数对象,事实上,CPython在编译py脚本时(编译成bytecodes),还会做其它与文档说明不符的优化,StackOverflow上的这篇帖子Weird Integer Cache inside Python
2.6对此做了详细说明,值得研读。
总之,解释器的实现细节我们无法干预,但是,在编写应用程序时,我们要确保函数逻辑不会依赖“解释器会缓存小整数”的这个特性,以免踩到诡异的坑。
===================== EOF =======================
【Python笔记】从一个“古怪”的case探究CPython对Int对象的实现细节
原文:http://blog.csdn.net/slvher/article/details/44704407