Как/почему работает функция set() в {frozenset()}?
Несмотря на то, что наборы не сотрясаются, проверка членства в другом наборе работает:
>>> set() in {frozenset()}
True
Я ожидал TypeError: unhashable type: 'set'
, совместимый с другими поведениями в Python:
>>> set() in {} # doesn't work when checking in dict
TypeError: unhashable type: 'set'
>>> {} in {frozenset()} # looking up some other unhashable type doesn't work
TypeError: unhashable type: 'dict'
Итак, как настроено членство в другом наборе?
Ответы
Ответ 1
В последней строке документации для set
s говорится следующее:
Обратите внимание, что аргумент elem
__contains__()
, remove()
и discard()
может быть set
. Для поддержки поиска эквивалентного параметра frozenset
временный создается из elem
.
Ответ 2
set_contains
реализуется следующим образом:
static int
set_contains(PySetObject *so, PyObject *key)
{
PyObject *tmpkey;
int rv;
rv = set_contains_key(so, key);
if (rv < 0) {
if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
return -1;
PyErr_Clear();
tmpkey = make_new_set(&PyFrozenSet_Type, key);
if (tmpkey == NULL)
return -1;
rv = set_contains_key(so, tmpkey);
Py_DECREF(tmpkey);
}
return rv;
}
Таким образом, это будет делегировать непосредственно set_contains_key
который будет по существу хешировать объект, а затем искать элемент, используя его хэш.
Если объект не сотрясается, set_contains_key
возвращает -1
, поэтому мы получаем внутри if
. Здесь мы явно проверяем, является ли переданный key
объект множеством (или экземпляром установленного подтипа), и мы ранее получили ошибку типа. Это предполагает, что мы попытались проверить сдерживание с помощью set
но это не удалось, потому что оно не сотрясается.
В этой точной ситуации мы теперь создаем новый frozenset
из этого set
и снова set_contains_key
проверить сдерживание с помощью set_contains_key
. И поскольку фризонсеты правильно хешируются, мы можем найти наш результат таким образом.
Это объясняет, почему следующие примеры будут работать должным образом, даже если сам набор не хешируется:
>>> set() in {frozenset()}
True
>>> set(('a')) in { frozenset(('a')) }
True