Python 2.7如何比较列表中的项目

bhe*_*ilr 7 python 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
Run Code Online (Sandbox Code Playgroud)
>>> 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
Run Code Online (Sandbox Code Playgroud)

因此,似乎容器内的比较在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
Run Code Online (Sandbox Code Playgroud)
>>> eq = TestableEq2()
>>> [eq] == [eq]
True
>>> eq.eq_run
False
>>> eq == eq
False
>>> eq.eq_run
True
Run Code Online (Sandbox Code Playgroud)

这是否意味着Python is在容器的实现中__eq__使用?有没有解决的办法?

我的用例是我正在构建一个继承自某些collectionsABCs 的数据结构,我想编写测试以确保我的结构正常运行.我认为注入比较时记录的值会很简单,但令我惊讶的是,检查时检查失败,以确保进行比较.

编辑:我应该提到这是在Python 2.7上,但我在3.3上看到了相同的行为.

Reu*_*ani 14

==如果项目相同(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;
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样,只要RichCompareBool1(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
Run Code Online (Sandbox Code Playgroud)

要覆盖它,您必须手动比较项目.

  • 万岁前往源头! (4认同)
  • 我认为它不会比这更明确.文档清楚地表明我得到了预期的行为,并且只有当对象具有不同的`id`s时才会调用`__eq__`,并且源支持该声明. (2认同)

jon*_*rpe 10

Python对序列相等性的测试如下:

                 Lists identical?
                  /          \  
                 Y            N
                /              \
             Equal         Same length?
                            /       \  
                           Y         N
                          /           \
                  Items identical?   Not equal
                     /       \
                    Y         N
                   /           \
                Equal      Items equal?
                            /        \
                           Y          N
                          /            \
                       Equal        Not equal
Run Code Online (Sandbox Code Playgroud)

您可以看到,只有当两个序列的长度相同但每个位置的项目不相同时,才会测试每个位置的项目是否相等.如果要强制使用等式检查,则需要例如:

all(item1 == item2 for item1, item2 in zip(list1, list2))
Run Code Online (Sandbox Code Playgroud)

  • 这很容易理解,很好的工作. (2认同)