Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Вы также должны определить __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__().

Ответ 2

Установить __contains__ делает проверки в следующем порядке:

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

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

Ответ 3

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

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

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

Ответ 4

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

Благодаря помощи, которую я получил в этом вопросе SO, я смог преследовать оператора in через исходный код root. В нижней части я обнаружил функцию 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;
}

Ответ 5

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

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

Кажется, что происходит:

  • Хэш-код вычисляется, когда элемент вставлен в хеш. (Для сравнения с существующими элементами.)
  • Вычисляется хэш-код объекта, который вы проверяете с помощью оператора in.
  • Элементы набора с одним и тем же хэш-кодом проверяются, сначала проверяя, являются ли они тем же объектом, что и тот, который вы ищете, или если они логически равны ему.