内容简介:但反过来就不成立了,如果定义所以,绝大多数情况下,重写
起步
__eq__
是用来重载操作符 ==
的;相对的, __ne__
用来重载 !=
。这两个魔术方法存在一个容易忽略的隐藏关系,就是当类中定义了 __eq__
而没有定义 __ne__
的时候,对于 !=
操作会取 __ne__()
的反向结果。
但反过来就不成立了,如果定义 __ne__
而没有定义 __eq__
,对于 ==
操作则会进行默认的行为。也就是说 x != y
不一定就是 x == y
的相反结果。它们之间并不存在这样的隐藏调用的关系。
所以,绝大多数情况下,重写 __eq__
方法就够了。
其他比较操作符:
object.__lt__(self, other) object.__le__(self, other) object.__gt__(self, other) object.__ge__(self, other)
也都不存在这个关系。没定义的操作符会抛出 NotImplemented
异常。
引发的思考
在 python 中所有类都继承自 object
,而 object.__ne__
是存在的:
>>> class A: ... def __eq__(self, other): ... return True ... >>> id(object.__ne__) 2024343946008 >>> id(A.__ne__) 2024343946008 >>> a = object() >>> b = object() >>> a == b False >>> a != b True >>> a = A() >>> b = A() >>> a == b True >>> a != b False >>>
这里的问题是, A.__ne__
从基类继承过来,它明明就存在的啊,为什么这边没有去调用而是通过 A.__eq__
去判断呢,很奇怪是不是?
这要探究就得深入CPython中去了,执行 __ne__
是在 typeobject.c
的 object_richcompare
中:
static PyObject *
object_richcompare(PyObject *self, PyObject *other, int op)
{
PyObject *res;
switch (op) {
case Py_EQ:
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. */
// 上面的注释是,__ne__ 委托给 __eq__ 来执行,对结果取反
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;
}
return res;
}
这里注释写得很清楚了,重点在于 res = (*self->ob_type->tp_richcompare)(self, other, Py_EQ);
中执行了 PyObject_RichCompare
,这是一种性能比较低的比较方式,它实际上使用默认的逻辑 __eq__
,这部分在 object.c
的 do_richcompare
函数中。
static PyObject *
do_richcompare(PyObject *v, PyObject *w, int op)
{
richcmpfunc f;
PyObject *res;
int checked_reverse_op = 0;
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);
}
if ((f = v->ob_type->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
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);
}
/* If neither object implements it, provide a sensible default
for == and !=, but raise an exception for ordering. */
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;
}
Py_INCREF(res);
return res;
}
不管在 PyObject_RichCompare
还是在 do_richcompare
我都看到了 if (res != Py_NotImplemented)
这样的语句。难道我们可以在比较操作中返回 NotImplemented
?我不禁要试一下:
class A:
def __eq__(self, other):
return NotImplemented
def __ne__(self, other):
return NotImplemented
a = A()
b = A()
print(a == b) # True
print(a != b) # True
print(a != a) # False
显然这里的 __ne__
被实现,不会去调用 __eq__
了,而我们返回了 NotImplemented
,也就是告诉解释器这些方法不存在,这就让 do_richcompare
中的 3 个 if 都不成立,乖乖走后续的 switch
块,这部分就直接通过指针是否相等来判断了。
性能比较
通过 PyObject_RichCompare
来比较效率会比较低,甚至有的需要处理 NotImplemented
的情况。我们对这几种情况做个对比:
class Default:
pass
class NeOnly:
def __ne__(self, other):
return not self == other
class RNotImplemented:
def __ne__(self, other):
return NotImplemented
def c_level():
cl = Default()
return lambda: cl != cl
def high_level_python():
hlp = NeOnly()
return lambda: hlp != hlp
def low_level_python():
llp = RNotImplemented()
return lambda: llp != llp
import timeit
print(min(timeit.repeat(c_level()))) # 0.10529670296476416
print(min(timeit.repeat(high_level_python())))# 0.31792451405355615
print(min(timeit.repeat(low_level_python()))) # 0.3742690036398786
数字很有说服力。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法:C语言实现
塞奇威克 / 霍红卫 / 机械工业出版社 / 2009-10 / 79.00元
《算法:C语言实现(第1-4部分)基础知识、数据结构、排序及搜索(原书第3版)》细腻讲解计算机算法的C语言实现。全书分为四部分,共16章。包括基本算法分析原理,基本数据结构、抽象数据结构、递归和树等数据结构知识,选择排序、插入排序、冒泡排序、希尔排序、快速排序方法、归并和归并排序方法、优先队列与堆排序方法、基数排序方法以及特殊用途的排序方法,并比较了各种排序方法的性能特征,在进一步讲解符号表、树等......一起来看看 《算法:C语言实现》 这本书的介绍吧!