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