Python 2.7 如何比较列表中的项目 [英] How does Python 2.7 compare items inside a list

查看:61
本文介绍了Python 2.7 如何比较列表中的项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我今天遇到了这个有趣的例子

class TestableEq(object):def __init__(self):self.eq_run = Falsedef __eq__(自我,其他):self.eq_run = Trueif isinstance(other, TestableEq):other.eq_run = 真回归自我是他者

<小时><预><代码>>>>eq = TestableEq()>>>eq.eq_run错误的>>>eq == eq真的>>>eq.eq_run真的>>>eq = TestableEq()>>>eq 是 eq真的>>>eq.eq_run错误的>>>[eq] == [eq]真的>>>eq.eq_run # 应该是 True 吧?错误的>>>(eq,) == (eq,) # 也许用元组?真的>>>eq.eq_run错误的>>>{'eq': eq} == {'eq': eq} # dicts?真的>>>eq.eq_run错误的>>>import numpy as np # 肯定 NumPy 按预期工作>>>np.array([eq]) == np.array([eq])真的>>>eq.eq_run错误的

所以看起来容器内的比较在 Python 中的工作方式不同.我希望对 == 的调用将使用每个对象的 __eq__ 实现,否则有什么意义?另外

class TestableEq2(object):def __init__(self):self.eq_run = Falsedef __eq__(自我,其他):self.eq_run = Trueother.eq_run = 真返回错误

<小时><预><代码>>>>eq = TestableEq2()>>>[eq] == [eq]真的>>>eq.eq_run错误的>>>eq == eq错误的>>>eq.eq_run真的

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

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

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

解决方案

如果项目相同(is).

CPython 使用它作为优化,假设身份意味着相等.

这记录在 PyObject_RichCompareBool 中,它是用于比较项目:

<块引用>

注意:如果 o1 和 o2 是同一个对象,PyObject_RichCompareBool() 将始终为 Py_EQ 返回 1,为 Py_NE 返回 0.

来自 listobject.c 实现:

/* 搜索第一个不同项的索引 */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 为 1//因为 RichCmopareBool 的行为如果 (k <0)返回空;如果(!k)休息;}

如您所见,只要 RichCompareBool1 (True),则不会检查项目.

并且来自 object.c 对 <代码>PyObject_RichCompareBool:

/* 对象相同时的快速结果.保证身份意味着平等.*/如果(v == w){如果(操作 == Py_EQ)返回 1;否则如果(op == Py_NE)返回0;}//... 实际上是深度比较对象

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

I came across this interesting example today

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

So it seems that comparisons inside containers works differently in Python. I would expect that the call to == would use each object's implementation of __eq__, otherwise what's the point? Additionally

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

Does this mean that Python uses is from within container's implementations of __eq__ instead? Is there a way around this?

My use case is that I am building a data structure inheriting from some of the collections ABCs and I want to write tests to make sure my structure is behaving correctly. I figured it would be simple to inject a value that recorded when it was compared, but to my surprise the test failed when checking to ensure that comparison occurred.

EDIT: I should mention that this is on Python 2.7, but I see the same behavior on 3.3.

解决方案

CPython's underlying implementation will skip the equality check (==) for items in a list if items are identical (is).

CPython uses this as an optimization assuming identity implies equality.

This is documented in PyObject_RichCompareBool, which is used to compare items:

Note: If o1 and o2 are the same object, PyObject_RichCompareBool() will always return 1 for Py_EQ and 0 for Py_NE.

From the listobject.c implementation:

/* 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;
}

As you can see as long as RichCompareBool is 1 (True) the items are not checked.

And from object.c's implementation of 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

To override this you'll have to compare the items manually.

这篇关于Python 2.7 如何比较列表中的项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆