Должны ли экземпляры подкласса frozenset быть хэшируемыми в Python 3?
Согласно https://docs.python.org/2/library/stdtypes.html, в Python 2:
Тип frozenset является неизменным и хэшируемым - его содержимое не может быть изменено после создания; однако его можно использовать как ключ словаря или как элемент другого набора.
Однако, согласно https://docs.python.org/3.4/library/stdtypes.html, в Python 3 я не вижу никакой информации, указывающей на то, что экземпляр frozenset (или подкласс) должен быть хешируемым, только элементы set/frozenset:
Элементы набора, например ключи словаря, должны быть хэшируемыми.
Итак, должен ли следующий код работать для любого интерпретатора Python 3, или если последняя строка вызывает TypeError
?
# Code under test
class NewFrozenSet(frozenset):
def __eq__(self, other):
return True
# Workaround: Uncomment this override
# def __hash__(self):
# return hash(frozenset(self))
hash(frozenset())
hash(NewFrozenSet())
OSX Yosemite 10.10, система python2
$ python
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class NewFrozenSet(frozenset):
... def __eq__(self, other):
... return True
...
>>> hash(frozenset())
133156838395276
>>> hash(NewFrozenSet())
133156838395276
OSX Yosemite 10.10, с использованием доморощенного http://brew.sh/
$ brew install python3
$ python3
Python 3.4.2 (default, Jan 5 2015, 11:57:21)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class NewFrozenSet(frozenset):
... def __eq__(self, other):
... return True
...
>>> hash(frozenset())
133156838395276
>>> hash(NewFrozenSet())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'NewFrozenSet'
>>>
Ubuntu 14.04.1 LTS (x86_64), система python3
$ python3
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class NewFrozenSet(frozenset):
... def __eq__(self, other):
... return True
...
>>> hash(frozenset())
133156838395276
>>> hash(NewFrozenSet())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'NewFrozenSet'
>>>
TL; DR - это регрессия в Python 3, или это был осознанный выбор дизайна?
1 ответ
Из определения hashable:
Хэшируемые объекты, которые сравниваются равными, должны иметь одинаковое хеш-значение.
Вы внедрили новый __eq__
метод, но не новый __hash__
за NewFrozenSet
, Теперь Python не может предположить, что вышеприведенное свойство выполнено (то есть, что поведение __eq__
а также __hash__
соответствует), поэтому он не может предполагать, что тип является хэшируемым. Вот почему вам нужно реализовать оба __eq__
а также __hash__
сделать тип хэшируемым (или не реализовывать и не использовать методы из родительского класса).
Например, если вы пропустите __eq__
затем NewFrozenSet
становится хасируемым:
class NewFrozenSet(frozenset):
pass
Если это не правильно в вашем NewFrozenSet
тогда вам нужно реализовать оба __eq__
а также __hash__
,