设置“in”运算符:使用相等或身份?

问题描述 投票:30回答:5
class A(object):
    def __cmp__(self):
        print '__cmp__'
        return object.__cmp__(self)

    def __eq__(self, rhs):
        print '__eq__'
        return True
a1 = A()
a2 = A()
print a1 in set([a1])
print a1 in set([a2])

为什么第一行打印为True,但第二行打印为False?既不进入运营商eq?

我使用的是Python 2.6

python set identity operator-keyword equality
5个回答
15
投票

你也需要定义__hash__。例如

class A(object):
    def __hash__(self):
        print '__hash__'
        return 42

    def __cmp__(self, other):
        print '__cmp__'
        return object.__cmp__(self, other)

    def __eq__(self, rhs):
        print '__eq__'
        return True

a1 = A()
a2 = A()
print a1 in set([a1])
print a1 in set([a2])

将按预期工作。

作为一般规则,任何时候你实施__cmp__你应该实现一个__hash__,所有xy,如x == yx.__hash__() == y.__hash__()


16
投票

设置__contains__按以下顺序进行检查:

 'Match' if hash(a) == hash(b) and (a is b or a==b) else 'No Match'

相关的C源代码位于Objects / setobject.c :: set_lookkey()和Objects / object.c :: PyObject_RichCompareBool()中。


7
投票

集合和字典通过使用散列作为完全相等性检查的快速近似来获得其速度。如果要重新定义相等性,通常需要重新定义哈希算法以使其保持一致。

默认的哈希函数使用对象的标识,这对于完全相等的快速近似是相当无用的,但至少允许您使用任意类实例作为字典键并检索与其一起存储的值如果您准确传递与键相同的对象。但这意味着如果你重新定义相等并且不重新定义哈希函数,那么你的对象将进入字典/集合而不会抱怨不可用,但仍然不会按照你期望的方式工作。

有关详细信息,请参阅the official python docs on __hash__


1
投票

一个切实的答案,但你的问题和我的测试让我很好奇。如果你忽略了作为__hash__问题根源的集合运算符,那么事实证明你的问题仍然很有趣。

感谢this SO question的帮助,我能够通过源代码追逐in运算符到它的根目录。在底部附近,我找到了PyObject_RichCompareBool函数,它在测试相等性之前确实测试了身份(请参阅关于“快速结果”的注释)。

因此,除非我误解了事情的运作方式,否则通过平等测试本身,您的问题的技术答案首先是身份,然后是平等。重申一下,这不是您所看到的行为的来源,而只是您问题的技术答案。

如果我误解了消息来源,有人请我直截了当。

int
PyObject_RichCompareBool(PyObject *v, PyObject *w, int op)
{
    PyObject *res;
    int ok;

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

    res = PyObject_RichCompare(v, w, op);
    if (res == NULL)
        return -1;
    if (PyBool_Check(res))
        ok = (res == Py_True);
    else
        ok = PyObject_IsTrue(res);
    Py_DECREF(res);
    return ok;
}

0
投票

在比较相等性之前,集合似乎使用哈希码,然后使用身份。以下代码:

class A(object):
    def __eq__(self, rhs):
        print '__eq__'
        return True
    def __hash__(self):
        print '__hash__'
        return 1

a1 = A()
a2 = A()

print 'set1'
set1 = set([a1])

print 'set2'
set2 = set([a2])

print 'a1 in set1'
print a1 in set1

print 'a1 in set2'
print a1 in set2

输出:

set1
__hash__
set2
__hash__
a1 in set1
__hash__
True
a1 in set2
__hash__
__eq__
True

会发生什么似乎是:

  1. 当元素插入哈希时计算哈希码。 (与现有元素进行比较。)
  2. 将计算您使用in运算符检查的对象的哈希码。
  3. 通过首先检查它们是否与您正在查找的对象相同,或者它们在逻辑上是否等于它,来检查具有相同哈希码的集合的元素。
© www.soinside.com 2019 - 2024. All rights reserved.