frozenset 子类的实例是否应该在 Python 3 中可哈希?
Should instances of a frozenset subclass be hashable in Python 3?
根据https://docs.python.org/2/library/stdtypes.html#frozenset,在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.
然而,根据 https://docs.python.org/3.4/library/stdtypes.html#frozenset,在 Python 3 中,我看不到任何信息表明 frozenset 实例(或子类)应该是可哈希的,只有 set/frozenset 元素:
Set elements, like dictionary keys, must be hashable.
那么,下面的代码应该适用于任何 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 中的回归,还是故意的设计选择?
根据hashable的定义:
Hashable objects which compare equal must have the same hash value.
您现在已经实现了一个新的 __eq__
方法,但没有为 NewFrozenSet
实现一个新的 __hash__
方法。 Python 现在不能假设上面的 属性 成立(即 __eq__
和 __hash__
的行为匹配)所以它不能假设类型是可散列的。这就是为什么您需要同时实现 __eq__
和 __hash__
以使类型可哈希(或者不实现任何一个并使用父 class 中的方法)的原因。
例如,如果您省略 __eq__
,那么 NewFrozenSet
会变得可哈希:
class NewFrozenSet(frozenset):
pass
如果这在您的 NewFrozenSet
中不正确,那么您需要同时实施 __eq__
和 __hash__
。
根据https://docs.python.org/2/library/stdtypes.html#frozenset,在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.
然而,根据 https://docs.python.org/3.4/library/stdtypes.html#frozenset,在 Python 3 中,我看不到任何信息表明 frozenset 实例(或子类)应该是可哈希的,只有 set/frozenset 元素:
Set elements, like dictionary keys, must be hashable.
那么,下面的代码应该适用于任何 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 中的回归,还是故意的设计选择?
根据hashable的定义:
Hashable objects which compare equal must have the same hash value.
您现在已经实现了一个新的 __eq__
方法,但没有为 NewFrozenSet
实现一个新的 __hash__
方法。 Python 现在不能假设上面的 属性 成立(即 __eq__
和 __hash__
的行为匹配)所以它不能假设类型是可散列的。这就是为什么您需要同时实现 __eq__
和 __hash__
以使类型可哈希(或者不实现任何一个并使用父 class 中的方法)的原因。
例如,如果您省略 __eq__
,那么 NewFrozenSet
会变得可哈希:
class NewFrozenSet(frozenset):
pass
如果这在您的 NewFrozenSet
中不正确,那么您需要同时实施 __eq__
和 __hash__
。