Python set" in " оператор: использует равенство или идентичность?

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? И ни входит оператор эквалайзер?

Я использую Python 2.6

5 ответов


вам нужно определить __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__ такие, что для всех x и y такое, что x == y, x.__hash__() == y.__hash__().


Набор __содержит__ производит проверку в следующем порядке:

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

соответствующий исходный код C находится в Objects / setobject.c:: set_lookkey() и в объектах / объекте.c:: PyObject_RichCompareBool().


наборы и словари набирают скорость с помощью хеширования как быстрое приближение проверки полного равенства. Если вы хотите переопределить равенство, вам обычно нужно переопределить алгоритм хэша, чтобы он был согласованным.

хэш-функция по умолчанию использует идентификатор объекта, что довольно бесполезно в качестве быстрого приближения полного равенства, но, по крайней мере, позволяет использовать произвольный экземпляр класса в качестве словарного ключа и извлекать значение, хранящееся с это если передать точно такой же объект, как и ключ. Но это означает, если вы переопределите равенство и не переопределите хэш-функцию, ваши объекты войдут в словарь / набор, не жалуясь на то, что они не хэшируются, но все равно не будут работать так, как вы ожидаете.

посмотреть официальные документы python на __hash__ для получения более подробной информации.


тангенциальный ответ, но ваш вопрос и мое тестирование вызвали у меня любопытство. Если вы игнорируете оператор set, который является источником вашего __hash__ проблема, оказывается, ваш вопрос по-прежнему интересен.

благодаря помощи, которую я получил на это так вопрос, я смог преследовать оператора 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;
}

наборы, похоже, используют хэш-коды, а затем идентичность, прежде чем сравнивать для равенства. Следующий код:

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. элементы набора с тем же хэш-кодом сначала проверьте, являются ли они тем же объектом, что и тот, который вы ищете, или логически равны ему.