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

时间:2012-02-01 01:10:59

标签: python set identity operator-keyword equality

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

5 个答案:

答案 0 :(得分: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()中。

答案 1 :(得分: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__() {1}}。

答案 2 :(得分:7)

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

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

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

答案 3 :(得分: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;
}

答案 4 :(得分: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. 检查具有相同哈希码的集合的元素,首先检查它们是否与您正在寻找的对象相同,或者它们在逻辑上是否等于它。