“可哈希”在Python中是什么意思?


Answers:


180

Python词汇表中

如果对象的哈希值在其生命周期内始终不变(需要一个__hash__()方法),并且可以与其他对象进行比较(需要一个__eq__()or __cmp__()方法),则该对象是可哈希的。比较相等的可哈希对象必须具有相同的哈希值。

散列性使对象可用作字典键和set成员,因为这些数据结构在内部使用散列值。

Python的所有不可变内置对象都是可哈希的,而没有可变容器(例如列表或字典)是可哈希的。作为用户定义类实例的对象默认情况下可哈希化;它们都比较不相等,并且其哈希值是id()


2
如果它hash value现在具有什么是哈希值。您能举个例子吗
user1755071 2013年

2
@ user55711:在这里,哈希值是调用的结果__hash__()。更一般而言,请参见en.wikipedia.org/wiki/Hash_function
NPE

16
@TorstenBronger:因为两个不相等的对象可以哈希为相同的值。换句话说,散列是有损的。
NPE 2015年

1
在python-2.7.12中,的结果id(object)是的结果的16倍object.__hash__()。因此,此版本的词汇表摘录不正确-哈希值不是id(),而是从其派生的(确实在python 2.7.12的更新文档中已指出)。
davidA

2
我知道这是一篇老文章,但是值得一提的是,此处复制的词汇表条目并不完全正确。您可以将可变对象(如列表)放在元组中。元组仍然是不可变的,但是您可以更改其中的列表,因此它不可哈希。尝试hash((1, [2, 3]))看看它的作用。我已经发布了一个要求更正词汇表条目中可哈希值的请求。
约翰·里尔

102

这里的所有答案都很好地解释了python中可哈希对象的工作原理,但是我相信必须首先了解术语“哈希”。

散列是计算机科学中的一个概念,用于创建高性能的伪随机访问数据结构,在该结构中要快速存储和访问大量数据。

例如,如果您有10,000个电话号码,并且想要将它们存储在一个数组中(这是一个顺序数据结构,可将数据存储在连续的内存位置中,并提供随机访问),但是您可能没有所需的连续数量内存位置。

因此,您可以改为使用大小为100的数组,并使用哈希函数将一组值映射到相同的索引,并且这些值可以存储在链接列表中。这提供了类似于阵列的性能。

现在,哈希函数可以很简单,只需将数字除以数组的大小,然后将其余部分作为索引即可。

有关更多详细信息,请参阅https://en.wikipedia.org/wiki/Hash_function

这是另一个很好的参考:http : //interactivepython.org/runestone/static/pythonds/SortSearch/Hashing.html


1
这是关于哈希的有趣观点。我还没有那样想。
yuvgin '19

@yuvgin哈希表通常用于实现稀疏数组(即此处给出的示例)。
Eli Korvigo

@EliKorvigo我想将常规数组视为哈希表的高度优化版本。
Mark Ransom

1
您能否针对电话号码数组方案生成一些简单的代码来阐明哈希的概念?
Istiaque Ahmed

18

任何不可变的东西(可变的手段,可能会改变)都可以被散列。除了要查找的哈希函数(如果有类)之外,还可以通过例如。dir(tuple)寻找__hash__方法,这里有一些例子

#x = hash(set([1,2])) #set unhashable
x = hash(frozenset([1,2])) #hashable
#x = hash(([1,2], [2,3])) #tuple of mutable objects, unhashable
x = hash((1,2,3)) #tuple of immutable objects, hashable
#x = hash()
#x = hash({1,2}) #list of mutable objects, unhashable
#x = hash([1,2,3]) #list of immutable objects, unhashable

不可变类型列表:

int, float, decimal, complex, bool, string, tuple, range, frozenset, bytes

可变类型列表:

list, dict, set, bytearray, user-defined classes

我最近发现,Ellipsis也是不可变的类型,可以用作的键dict
加博尔·费克特

甚至可以使用用户定义的类,但只能使用其名称,而不能使用实例。例如:hash(MyClass)
的Gabor菲克特

1
如果用户定义的类的实例实现__hash__和,则它们的@GáborFekete实例是可哈希的__eq__。而且,所有用户定义的类都实现了这些方法(因此可以哈希化),因为它们从object(通用基类)继承了这些方法。
Eli Korvigo

6

根据Python词汇表的理解,当您创建可哈希对象的实例时,还会根据实例的成员或值来计算不可更改的值。例如,该值随后可以用作字典中的键,如下所示:

>>> tuple_a = (1,2,3)
>>> tuple_a.__hash__()
2528502973977326415
>>> tuple_b = (2,3,4)
>>> tuple_b.__hash__()
3789705017596477050
>>> tuple_c = (1,2,3)
>>> tuple_c.__hash__()
2528502973977326415
>>> id(a) == id(c)  # a and c same object?
False
>>> a.__hash__() == c.__hash__()  # a and c same value?
True
>>> dict_a = {}
>>> dict_a[tuple_a] = 'hiahia'
>>> dict_a[tuple_c]
'hiahia'

我们可以发现tuple_a和tuple_c的哈希值相同,因为它们具有相同的成员。当我们将tuple_a用作dict_a中的键时,我们可以发现dict_a [tuple_c]的值相同,这意味着,当它们用作dict中的键时,它们将返回相同的值,因为哈希值是相同。对于那些不可哈希的对象,方法哈希定义为“无”:

>>> type(dict.__hash__) 
<class 'NoneType'>

我猜这个哈希值是在实例初始化时计算出来的,而不是以动态方式计算的,这就是为什么只有不可变对象才可以哈希的原因。希望这可以帮助。


4

让我给您一个工作示例,以了解python中的可哈希对象。我以2个元组为例,一个元组中的每个值都有一个唯一的哈希值,该值在其生命周期内不会改变。因此,基于此值,可以完成两个元组之间的比较。我们可以使用Id()获得元组元素的哈希值。

2个元组之间的比较2个元组之间的等效性


26
这将是文本而不是图像更有用
baxx

6
这是一个错误的答案。id()显示内存中的引用地址,它不是哈希值。为了获得哈希,请使用__hash __()函数。例如:t1 .__ hash __()
Vlad

@ascentman不要犹豫,编辑您认为错误的答案。您的编辑将被同行评审,如果被接受,您将获得一小分的奖励。
XavierStuvw

4

在python中,这意味着该对象可以是集合的成员以便返回索引。即,它们具有唯一的身份/ ID。

例如,在python 3.3中:

数据结构列表不可散列,但数据结构元组可散列。


哈希值与id大约不同,后者是(大约)对象在内存中的地址。
poolie

3

可散列=能够被散列。

好的,什么是哈希?哈希函数是一种函数,它接受一个对象(例如字符串,例如“ Python”)并返回固定大小的代码。为简单起见,假定返回值为整数。

当我在Python 3中运行hash('Python')时,结果为5952713340227947791。不同版本的Python可以自由更改基础哈希函数,因此您可能会获得不同的值。重要的是,无论我现在多次运行hash('Python'),还是始终使用相同版本的Python获得相同的结果。

但是hash('Java')返回1753925553814008565。因此,如果要散列的对象发生了变化,结果也将发生变化。另一方面,如果我正在哈希的对象没有更改,则结果保持不变。

为什么这么重要?

好吧,例如,Python字典要求键是不可变的。即,键必须是不变的对象。字符串在Python中是不变的,其他基本类型(int,float,bool)也是如此。元组和冻结集也是不可变的。另一方面,列表不是不可变的(即,它们是可变的),因为您可以更改它们。同样,字典是易变的。

因此,当我们说某事是可哈希的时,我们表示它是不可变的。如果我尝试将可变类型传递给hash()函数,它将失败:

>>> hash('Python')
1687380313081734297
>>> hash('Java')
1753925553814008565
>>>
>>> hash([1, 2])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> hash({1, 2})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
>>> hash({1 : 2})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>>
>>> hash(frozenset({1, 2}))
-1834016341293975159
>>> hash((1, 2))
3713081631934410656

1
请注意,python在每个过程开始时随机植入哈希算法。因此,如果您在不同的进程中运行两次hash('Python'),则实际上将获得不同的哈希值。
D哈德森

2

在Python中,任何不可变的对象(例如整数,布尔值,字符串,元组)都是可哈希的,这意味着其值在其生存期内不会改变。这使Python可以创建一个唯一的哈希值来标识它,字典可以使用它来跟踪唯一的键,并使用集合来跟踪唯一的值。

这就是为什么Python要求我们对字典中的键使用不可变的数据类型。


-1

为了从头开始创建哈希表,必须将所有值设置为“无”,并在需要时进行修改。哈希对象是指可修改的数据类型(字典,列表等)。另一方面,一旦分配,就无法重新初始化集合,因此集合不可散列。而set()的变体-Frozenset()是可哈希的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.