== 和 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



12345678910111213
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



1
static PyObject * cmp_outcome(int op, PyObject *v, PyObject *w);

is比较的本质


先看 cmp_outcome 函数中处理 is 和 is not 的部分:



123456789101112
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 都提供了一个重载方法和一个参数码,对应关系如下

1234567
符号    重载方法    参数码<       __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 的:

1234567891011
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;  }


这里分为几种情况:

  1. v 和 w 类型不同,w 是 v 的子类,w 如果重载了某个 richcompare 方法,则调用 w 中的 richcompare 方法:
123456789
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
  1. v 和 w 类型不同,或者 w 不是 v 的子类,或者 w 没有相应的 richcompare 方法,如果 v 定义了相应的 richcompare 方法,就调用 v 中相应的 richcompare 方法:
123456
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
  1. w 不是 v 的子类,v 中没有定义或者继承相应的 richcompare 方法而 w 中定义了相应的 richcompare 方法,就调用 w 中相应的 richcompare 方法:
123456
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);}


例子,接第二种情况的例子:

12345
In [12]: c == deq richcompare in AIn [13]: b == deq richcompare in D


以上三种情况总结起来就是:如果 w 是 v 的子类对象,优先调用 w 相应的 richcompare 方法,否则,v 和和 w 中谁有就调用谁的。

  1. 如果 v 和 w 都没有相应的 richcompare 方法,那么默认的处理是:
123456789101112131415
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 函数:

12345
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 -