== 和 is 的区别这个问题对于使用过 Python 一段时间开发人员的来说相信不是一个困难的问题。
本文将剖析 Python3.6 源码,旨在从实现细节层面把这个问题说清楚。从字节码看起
我们先来看看 == 和 is 编译后字节码的区别:
123456789101112131415161718192021222324252627
| In [1]: def test(): ...: a = 1 ...: b = 1 ...: a == b ...: a is b ...:In [2]: import disIn [3]: dis.dis(test) 2 0 LOAD_CONST 1 (1) 2 STORE_FAST 0 (a) 3 4 LOAD_CONST 1 (1) 6 STORE_FAST 1 (b) 4 8 LOAD_FAST 0 (a) 10 LOAD_FAST 1 (b) 12 COMPARE_OP 2 (==) 14 POP_TOP 5 16 LOAD_FAST 0 (a) 18 LOAD_FAST 1 (b) 20 COMPARE_OP 8 (is) 22 POP_TOP 24 LOAD_CONST 0 (None) 26 RETURN_VALUE
|
从字节码可以看出来,is
和 ==
都是交给 COMPARE_OP
来执行的,通过 oparg(== 是 2,is 是 8) 参数执行不同的处理,顺藤摸瓜,我们来到 COMPARE_OP
:
| TARGET(COMPARE_OP) { PyObject *right = POP(); PyObject *left = TOP(); PyObject *res = cmp_outcome(oparg, left, right); Py_DECREF(left); Py_DECREF(right); SET_TOP(res); if (res == NULL) goto error; PREDICT(POP_JUMP_IF_FALSE); PREDICT(POP_JUMP_IF_TRUE); DISPATCH();}
|
COMPARE_OP
将待比较的对象和参数又传入到 cmp_outcome
:
| static PyObject * cmp_outcome(int op, PyObject *v, PyObject *w);
|
is比较的本质
先看 cmp_outcome
函数中处理 is
和 is not
的部分:
| static PyObject *cmp_outcome(int op, register PyObject *v, register PyObject *w) { int res = 0; switch (op) { case PyCmp_IS: res = (v == w); break; case PyCmp_IS_NOT: res = (v != w); break; ...}
|
可以看出,is 和 is not 比较的就是 v 和 w 这俩个指针变量!而指针变量本质上是一个内存地址,它在 32 位系统中就是一个 32 整数,在 64 位系统中就是一个 64 位整数。
由此我们可以得出一个结论:is 比较的是俩个对象在内存中是否是同一个地址,换句话说,它们是否是同一个对象。
richcompare
继续往下之前,先来了解下 Python 中 richcompare 的概念。
其实不单单是 Python,编程语言应该都会提供 ,>= 这六中比较,在 Python 源码中,它们统称为 richcompare。
每一个比较,Python 都提供了一个重载方法和一个参数码,对应关系如下
| 符号 重载方法 参数码< __lt__ 0<= __le__ 1== __eq__ 2!= __le__ 3> __gt__ 4>= __ge__ 5
|
可以通过重写上面任意一个或多个方法来重载对应的操作符号,
Python 中每个对象都关联一个类型,类型中有一个 tp_richcompare
函数指针来决定对象之间做 rich compare 时的行为,所有对象的基类型提供了一个默认的实现,我们将在后面介绍。
==比较的本质
== 比较只是 richcompare 的一种,所有 richcompare 的比较最终都是要交给这个对象关联类型的
tp_richcompare
。
大部分内置类型,如 int,list,dict 都重写了这个函数,对于用户自建的类型,会优先调用用户重载的方法,没有再调用默认的
tp_richcompare
。这只是大概的逻辑,具体到细节,相同类型对象之间、不同类型对象之间、对象与其基类对象之间的比较又有所差异。
继续往下看
cmp_outcome
是怎么处理 richcompare 的:
| static PyObject *cmp_outcome(int op, register PyObject *v, register PyObject *w){ ... default: return PyObject_RichCompare(v, w, op); } v = res ? Py_True : Py_False; Py_INCREF(v); return v;}
|
richcompare 会进入到 PyObject_RichCompare
:
1234567891011121314151617
| PyObject *PyObject_RichCompare(PyObject *v, PyObject *w, int op){ PyObject *res; assert(Py_LT <= op && op <= Py_GE); if (v == NULL || w == NULL) { if (!PyErr_Occurred()) PyErr_BadInternalCall(); return NULL; } if (Py_EnterRecursiveCall(" in comparison")) return NULL; res = do_richcompare(v, w, op); Py_LeaveRecursiveCall(); return res;}
|
这个函数主要是对参数的检查,真正做事的是 do_richcompare
:
123456789101112131415161718192021
| /* Perform a rich comparison, raising TypeError when the requested comparison operator is not supported. */ static PyObject * do_richcompare(PyObject *v, PyObject *w, int op) { richcmpfunc f; PyObject *res; int checked_reverse_op = 0; /* 第一种情况 */ ... /* 第二种情况 */ ... /* 第三种情况 */ ... /* 第四种情况 */ ... Py_INCREF(res); return res; }
|
这里分为几种情况:
- v 和 w 类型不同,w 是 v 的子类,w 如果重载了某个 richcompare 方法,则调用 w 中的 richcompare 方法:
| if (v->ob_type != w->ob_type && PyType_IsSubtype(w->ob_type, v->ob_type) && (f = w->ob_type->tp_richcompare) != NULL) { checked_reverse_op = 1; res = (*f)(w, v, _Py_SwappedOp[op]); if (res != Py_NotImplemented) return res; Py_DECREF(res); }
|
例子:
1234567891011121314151617
| In [1]: class A: ...: pass ...:In [2]: class B(A): ...: def __eq__(self, o): ...: print('eq richcompare in B') ...: return True ...:In [3]: a = A()In [4]: b = B()In [5]: a == beq richcompare in BOut[5]: True
|
- v 和 w 类型不同,或者 w 不是 v 的子类,或者 w 没有相应的 richcompare 方法,如果 v 定义了相应的 richcompare 方法,就调用 v 中相应的 richcompare 方法:
| if ((f = v->ob_type->tp_richcompare) != NULL) { res = (*f)(v, w, op); if (res != Py_NotImplemented) return res; Py_DECREF(res); }
|
例子:
12345678910111213141516171819202122232425262728293031323334
| In [1]: class A: ...: def __eq__(self, o): ...: print('eq richcompare in A') ...:In [2]: class B: ...: pass ...:In [3]: class C(A): ...: pass ...:In [4]: class D(B): ...: def __eq__(self, o): ...: print('eq richcompare in D') ...:In [5]: a = A()In [6]: b = B()In [7]: c = C()In [8]: d = D()In [9]: a == beq richcompare in AIn [10]: a == ceq richcompare in AIn [11]: a == deq richcompare in A
|
- w 不是 v 的子类,v 中没有定义或者继承相应的 richcompare 方法而 w 中定义了相应的 richcompare 方法,就调用 w 中相应的 richcompare 方法:
| if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) { res = (*f)(w, v, _Py_SwappedOp[op]); if (res != Py_NotImplemented) return res; Py_DECREF(res);}
|
例子,接第二种情况的例子:
| In [12]: c == deq richcompare in AIn [13]: b == deq richcompare in D
|
以上三种情况总结起来就是:如果 w 是 v 的子类对象,优先调用 w 相应的 richcompare 方法,否则,v 和和 w 中谁有就调用谁的。
- 如果 v 和 w 都没有相应的 richcompare 方法,那么默认的处理是:
| switch (op) {case Py_EQ: res = (v == w) ? Py_True : Py_False; break;case Py_NE: res = (v != w) ? Py_True : Py_False; break;default: PyErr_Format(PyExc_TypeError, "'%s' not supported between instances of '%.100s' and '%.100s'", opstrings[op], v->ob_type->tp_name, w->ob_type->tp_name); return NULL;}
|
可以看到如果比较的是 == 和 !=,结果又回到 v 和 w 指针变量的直接比较,和 is 比较的结果相同,否则会引发一个类型错误。
例子:
12345678910111213141516171819202122232425262728293031
| In [1]: class A: ...: pass ...:In [2]: class B: ...: pass ...:In [3]: a = A()In [4]: b = B()In [5]: a == bOut[5]: FalseIn [6]: a is bOut[6]: FalseIn [7]: a != bOut[7]: TrueIn [8]: a is not bOut[8]: TrueIn [9]: a > b---------------------------------------------------------------------------TypeError Traceback (most recent call last)-9-1e61210fe837> in ()----> 1 a > bTypeError: '>' not supported between instances of 'A' and 'B'
|
object的默认richcompare
所有的类的基类
object
提供了一个默认的 richcompare 函数:
| PyTypeObject PyBaseObject_Type = { ... object_richcompare, /* tp_richcompare */ ...}
|
实现如下:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
| static PyObject *object_richcompare(PyObject *self, PyObject *other, int op){ PyObject *res; switch (op) { case Py_EQ: /* Return NotImplemented instead of False, so if two objects are compared, both get a chance at the comparison. See issue #1393. */ res = (self == other) ? Py_True : Py_NotImplemented; Py_INCREF(res); break; case Py_NE: /* By default, __ne__() delegates to __eq__() and inverts the result, unless the latter returns NotImplemented. */ if (self->ob_type->tp_richcompare == NULL) { res = Py_NotImplemented; Py_INCREF(res); break; } res = (*self->ob_type->tp_richcompare)(self, other, Py_EQ); if (res != NULL && res != Py_NotImplemented) { int ok = PyObject_IsTrue(res); Py_DECREF(res); if (ok < 0) res = NULL; else { if (ok) res = Py_False; else res = Py_True; Py_INCREF(res); } } break; default: res = Py_NotImplemented; Py_INCREF(res); break; } return res;}
|
可以看出,对于俩个相同类型的对象而言,== 默认比较的内存地址是否相同,即会否是同一个对象。对于 !=,如果类没有重载 !=(实现 ne),返回 Py_NotImplemented,这时候又回到上面的第 4 种情况,继续比较内存地址。其他比较也是回到上述第 4 中情况,引发类型错误。
例子:
12345678910111213141516171819202122232425262728293031323334353637383940414243
| In [1]: class A: ...: pass ...:In [2]: class B: ...: def __ne__(self, o): ...: print('ne richcompare in B') ...: return False ...:In [3]: a1 = A()In [4]: a2 = A()In [5]: a3 = a1In [6]: a1 == a2Out[6]: FalseIn [7]: a1 == a3Out[7]: TrueIn [8]: a1 != a2Out[8]: TrueIn [9]: b1 = B()In [10]: b2 = B()In [11]: b1 == b2Out[11]: FalseIn [12]: b1 != b2ne richcompare in BOut[12]: FalseIn [13]: a1 <= a2---------------------------------------------------------------------------TypeError Traceback (most recent call last)-13-7ba1adc4bd61> in <module>()----> 1 a1 <= a2TypeError: '<=' not supported between instances of 'A' and 'A'
|
总结
本文深入源码,剖析了 is 和 == 的区别和联系,总的来说就是:
- is 比较的是俩个对象内存地址是不是一样,即是否是同一个对象
- == 是 richcompare 的一种,除非对象的类型重写了 tp_richcompare,否则默认的 == 比较的也是俩个对象的内存地址,和 is 一致
Python 的常用内置类型如 int,string,list,dict 都有默认实现的 tp_richcompare 实现,这个可以另写一篇文章介绍了。
此外,与 Python2 相比,整个比较的逻辑是做了简化的,这里就不剖析 Python2 了,只提一点,Python2 中用户是可以通过重写 cmp 方法来决定对象之间的比较逻辑的,从 Python 3.0.1 版本后,这个方法被移除了。
- END -