Python 2.7如何比较列表中的项目
问题内容:
我今天遇到了这个有趣的例子
class TestableEq(object):
def __init__(self):
self.eq_run = False
def __eq__(self, other):
self.eq_run = True
if isinstance(other, TestableEq):
other.eq_run = True
return self is other
>>> eq = TestableEq()
>>> eq.eq_run
False
>>> eq == eq
True
>>> eq.eq_run
True
>>> eq = TestableEq()
>>> eq is eq
True
>>> eq.eq_run
False
>>> [eq] == [eq]
True
>>> eq.eq_run # Should be True, right?
False
>>> (eq,) == (eq,) # Maybe with tuples?
True
>>> eq.eq_run
False
>>> {'eq': eq} == {'eq': eq} # dicts?
True
>>> eq.eq_run
False
>>> import numpy as np # Surely NumPy works as expected
>>> np.array([eq]) == np.array([eq])
True
>>> eq.eq_run
False
因此,似乎容器内部的比较在Python中的工作方式有所不同。我希望对的调用==
将使用的每个对象的实现__eq__
,否则有什么意义呢?另外
class TestableEq2(object):
def __init__(self):
self.eq_run = False
def __eq__(self, other):
self.eq_run = True
other.eq_run = True
return False
>>> eq = TestableEq2()
>>> [eq] == [eq]
True
>>> eq.eq_run
False
>>> eq == eq
False
>>> eq.eq_run
True
这是否意味着Python会改用is
容器内部的实现__eq__
?有没有解决的办法?
我的用例是,我正在构建一个继承自某些collections
ABC的数据结构,并且我想编写测试以确保我的结构行为正确。我认为注入一个比较时记录的值很简单,但是令我惊讶的是,在检查以确保比较发生时测试失败。
编辑:我应该提到这是在Python 2.7上,但是我在3.3上看到了相同的行为。
问题答案:
==
如果项目相同(is
),CPython的基础实现将跳过列表中项目的相等性检查()。
CPython使用此作为优化,假设身份暗示平等。
这记录在PyObject_RichCompareBool中,用于比较项目:
注意:如果o1和o2是同一对象,则PyObject_RichCompareBool()对于Py_EQ将始终返回1,对于Py_NE将始终返回0。
从listobject.c实现中:
/* Search for the first index where items are different */
for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) {
int k = PyObject_RichCompareBool(vl->ob_item[i],
wl->ob_item[i], Py_EQ);
// k is 1 if objects are the same
// because of RichCmopareBool's behaviour
if (k < 0)
return NULL;
if (!k)
break;
}
正如你可以看到,只要RichCompareBool
是1
(True
)的项目不检查。
从object.c的实现PyObject_RichCompareBool
:
/* Quick result when objects are the same.
Guarantees that identity implies equality. */
if (v == w) {
if (op == Py_EQ)
return 1;
else if (op == Py_NE)
return 0;
}
// ... actually deep-compare objects
要覆盖此内容,您必须手动比较各项。