python里什么样的数据类型是可散列的?可散列也就是可以作为映射(字典、集合)里的键。
如果一个对象是可散列的,那么这个对象在它的生命周期内,它的散列值是不变的 ,而且这个对象需要实现__hash__方法,另外还要有__eq__方法,这样才能跟其他键比较。要注意两个对象相等,它们的散列值一定一样,但两个对象散列值一样它们可能不相等,这就是常说的散列表里的hash值冲突的情形。
不可变类型有可能是可散列的,但可变类型是不可散列的。原子不可变类型,像str、bytes和数值型(int、float等)都是可散列的,frozenset也是可散列的,因为根据定义frozenset里只能容纳可散列类型。然后像元组这样的不可变类型,它是不是可散列的取决于它的元素是不是可散列的。像其他的可变collection类型,如列表、集合和字典都是不可散列的,因为这些对象它们的内部状态是不稳定的,它们容纳的数据元素的数目是可动态变化的,哪怕它的数据元素都是可散列的,如果根据它们的数据元素来计算它们的散列值,不同状态下,它们的散列值可能是不同的,我们希望一个对象它在的整个生命周期内它的散列值是不变的。如果非要以可变对象计算散列值,可以取它们的id值为散列值,不过要注意观察这些对象是否已经生命周期结束,一旦如此要从映射里面删除掉以该对象的id值为散列值的项。
下面给出一个把可迭代或者字典对象变成一个可散列的元组对象的算法。
def is_iterable(x):
try:
iter(x)
except TypeError:
return False
else:
return True
def make_hashable(value):
if isinstance(value, dict):
return tuple(
[
(key, make_hashable(nested_value))
for key, nested_value in sorted(value.items())
]
)
try:
hash(value)
except TypeError:
if is_iterable(value):
return tuple(map(make_hashable, value))
raise
return value