ipython和python区别_深入 Python —— == 和 is 的区别

== 和 is 的区别这个问题对于使用过 Python 一段时间开发人员的来说相信不是一个困难的问题。本文将剖析 Python3.6 源码,旨在从实现细节层面把这个问题说清楚,

从字节码看起

我们先来看看 == 和 is 编译后字节码的区别:

In [1]: def test():

...: a = 1

...: b = 1

...: a == b

...: a is b

...:

In [2]: import dis

In [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:

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 :

/* Perform a rich comparison, raising TypeError when the requested comparisonoperator 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);

}

例子:

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

eq richcompare in B

Out[5]: Truev 和 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);

}

例子:

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

eq richcompare in A

In [10]: a == c

eq richcompare in A

In [11]: a == d

eq richcompare in Aw 不是 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 == d

eq richcompare in A

In [13]: b == d

eq 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 比较的结果相同,否则会引发一个类型错误。

例子:

In [1]: class A:

...: pass

...:

In [2]: class B:

...: pass

...:

In [3]: a = A()

In [4]: b = B()

In [5]: a == b

Out[5]: False

In [6]: a is b

Out[6]: False

In [7]: a != b

Out[7]: True

In [8]: a is not b

Out[8]: True

In [9]: a > b

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in ()

----> 1 a > b

TypeError: '>' not supported between instances of 'A' and 'B'

object 的默认 richcompare

所有的类的基类object 提供了一个默认的 richcompare 函数:

PyTypeObject PyBaseObject_Type = {

...

object_richcompare, /* tp_richcompare */

...

}

实现如下:

static PyObject *

object_richcompare(PyObject *self, PyObject *other, int op)

{

PyObject *res;

switch (op) {

case Py_EQ:

/* Return NotImplemented instead of False, so if twoobjects are compared, both get a chance at thecomparison. 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 中情况,引发类型错误。

例子:

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

In [6]: a1 == a2

Out[6]: False

In [7]: a1 == a3

Out[7]: True

In [8]: a1 != a2

Out[8]: True

In [9]: b1 = B()

In [10]: b2 = B()

In [11]: b1 == b2

Out[11]: False

In [12]: b1 != b2

ne richcompare in B

Out[12]: False

In [13]: a1 <= a2

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in ()

----> 1 a1 <= a2

TypeError: '<=' 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 版本后,这个方法被移除了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值