According to https://docs.python.org/2/library/stdtypes.html#frozenset, in Python 2:
The frozenset type is immutable and hashable -- its contents cannot be altered after is created; however, it can be used as a dictionary key or as an element of another set.
However according to https://docs.python.org/3.4/library/stdtypes.html#frozenset, in Python 3 I can see no information indicating that a frozenset instance (or subclass) should be hashable, just the set/frozenset elements:
Set elements, like dictionary keys, must be hashable.
So, should the following code work for any Python 3 interpreter, or should the last line raise a 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, system 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, using homebrew 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), system 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 - Is this a regression in Python 3, or was it a deliberate design choice?
From the definition of hashable:
Hashable objects which compare equal must have the same hash value.
You have now implemented a new __eq__
method but not a new __hash__
for NewFrozenSet
. Python now can't assume that the above property holds (i.e., that the behaviour of __eq__
and __hash__
matches) so it can't assume the type is hashable. That's why you need to implement both __eq__
and __hash__
to make a type hashable (or don't implement either and use the methods from the parent class).
For example, if you leave out __eq__
then NewFrozenSet
does become hashable:
class NewFrozenSet(frozenset):
pass
If this is not correct in your NewFrozenSet
then you need to implement both __eq__
and __hash__
.