简体   繁体   中英

Should instances of a frozenset subclass be hashable in Python 3?

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 (ie, 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__ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM