is和==都是对对象进行比较判断的操作符,但比较判断的内容并不相同。下面具体看看两者区别在哪?
基本概念
-
Python设计之初就是一门面向对象的语言,即一切皆对象
- 数组、字符串、元组、列表、字典、函数、方法、类、模块等等都是对象,包括代码
-
Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)
- 对象是堆上分配的一个内存空间,存储具体的对象值
- 变量是系统创建的表中的元素,拥有指向对象的引用
- 引用是从变量到对象的指针
-
技术上说,每个对象有两个标准的头部信息,一个类型标识符来标识类型,还有一个引用的计数器,用于决定是否需要回收对象
- 可以通过 sys 模块中的 getrefcount() 函数查询一个对象计数器的值
import sys sys.getrefcount(1)
具体区别
- is比较的是两个对象的id值是否相等,也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址
- ==比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法,==是python标准操作符中的比较操作符
a = [1, 2, 3]
b = a
print(id(a)) # 140275598064704
print(id(b)) # 140275598064704
print(b is a) # True
print(b == a) # True
b = a[:]
print(id(a)) # 140275598064704
print(id(b)) # 140275598064832
print(b is a) # False
print(b == a) # True
Python性能优化
- 出于对性能的考虑,Python内部做了很多的优化工作,对于整数对象,Python把一些频繁使用的整数对象缓存起来,保存到一个叫small_ints的链表中,在Python的整个生命周期内,任何需要引用这些整数对象的地方,都不再重新创建新的对象,而是直接引用缓存中的对象
- Python把可能频繁使用[-5, 256]之间的整数小对象放在small_ints中,但凡需要用些小整数时,就从这里面取,而不再去临时创建新的对象
a = 256
b = 128*2
print(a is b) # True
print(a == b) # True
a = 258
b = 129*2
print(a is b) # False
print(a == b) # True
small_ints源码分析(Python2.7)
- Python自身的main函数,会调用Py_Initialize函数初始化Python内部一系列模块,Modules/main.c,551行
- 初始化过程中,_PyInt_Init会被调用,Python/pythonrun.c,210行
- _PyInt_Init的唯一作用就是初始化small_ints数组,Objects/intobject.c,1452行,具体代码如下:
int
_PyInt_Init(void)
{
PyIntObject *v;
int ival;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {
if (!free_list && (free_list = fill_free_list()) == NULL)
return 0;
/* PyObject_New is inlined */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
small_ints[ival + NSMALLNEGINTS] = v;
}
#endif
return 1;
}
- NSMALLNEGINTS 和NSMALLPOSINTS 宏定义,Objects/intobject.c,68行,具体代码如下:
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
- Python诸如 a = 5 这样的语句,最终会落到PyInt_FromLong这个函数里,Objects/intobject.c,86行,具体代码如下:
PyObject *
PyInt_FromLong(long ival)
{
register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
#ifdef COUNT_ALLOCS
if (ival >= 0)
quick_int_allocs++;
else
quick_neg_int_allocs++;
#endif
return (PyObject *) v;
}
#endif
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}
- 从_PyInt_Init的实现上,我们可以看到被放入small_ints的数字范围是-5到256。因此,你可以通过修改源代码的方式,将这个范围任意的扩展。
small_ints源码(Python3.9)
- _PyLong_Init初始化small_ints数组,Objects/longobject.c,5754行,具体代码如下:
int
_PyLong_Init(PyThreadState *tstate)
{
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) {
sdigit ival = (sdigit)i - NSMALLNEGINTS;
int size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1);
PyLongObject *v = _PyLong_New(1);
if (!v) {
return -1;
}
Py_SET_SIZE(v, size);
v->ob_digit[0] = (digit)abs(ival);
tstate->interp->small_ints[i] = v;
}
#endif
if (_Py_IsMainInterpreter(tstate)) {
_PyLong_Zero = PyLong_FromLong(0);
if (_PyLong_Zero == NULL) {
return 0;
}
_PyLong_One = PyLong_FromLong(1);
if (_PyLong_One == NULL) {
return 0;
}
/* initialize int_info */
if (Int_InfoType.tp_name == NULL) {
if (PyStructSequence_InitType2(&Int_InfoType, &int_info_desc) < 0) {
return 0;
}
}
}
return 1;
}
- 两个宏:NSMALLNEGINTS 和NSMALLPOSINTS 的定义,Objects/longobject.c,20行,具体代码如下:
#define NSMALLPOSINTS _PY_NSMALLPOSINTS
#define NSMALLNEGINTS _PY_NSMALLNEGINTS
- 具体定义跳转至 Include/internal/pycore_interp.h,67行,具体代码如下:
#define _PY_NSMALLPOSINTS 257
#define _PY_NSMALLNEGINTS 5
特殊情况
- 示例A:
a = 'pythontab.com'
b = 'pythontab.com'
print(a is b) # False
print(a == b) # True
a = 'pythontabcom'
b = 'pythontabcom'
print(a is b) # True
print(a == b) # True
-
不同字符串类型is和==结果不完全相同,这个和解释器实现有关。
-
示例B:
a = (1, 2, 3)
b = (1, 2, 3)
print(a is b) # False
print(a == b) # True
a = [1, 2, 3]
b = [1, 2, 3]
print(a is b) # False
print(a == b) # True
a = {'python': 100, 'com': 1}
b = {'python': 100, 'com': 1}
print(a is b) # False
print(a == b) # True
a = set([1, 2, 3])
b = set([1, 2, 3])
print(a is b) # False
print(a == b) # True
- 当变量是数字、字符串、元组,列表,字典时,is和==都不相同,不能互换使用
- 当比较值时,要使用==,比较是否是同一个内存地址时应该使用is
扩展
- Java机制,缓存范围 -128~127
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.