hash在python中做什么?

Rom*_*man 64 python hash

我看到了一个代码示例,其中hash函数应用于元组.结果它返回一个负整数.我想知道这个功能是做什么的.谷歌没有帮助.我找到了一个页面,解释了如何计算哈希,但它没有解释为什么我们需要这个函数.

Len*_*bro 116

散列是固定大小的整数,用于标识特定值.每个值都需要有自己的哈希值,因此对于相同的值,即使它不是同一个对象,也会得到相同的哈希值.

>>> hash("Look at me!")
4343814758193556824
>>> f = "Look at me!"
>>> hash(f)
4343814758193556824
Run Code Online (Sandbox Code Playgroud)

需要以这样的方式创建散列值,使得结果值均匀分布,以减少您获得的散列冲突的数量.散列冲突是指两个不同的值具有相同的散列.因此,相对较小的变化通常会导致非常不同的哈希值.

>>> hash("Look at me!!")
6941904779894686356
Run Code Online (Sandbox Code Playgroud)

这些数字非常有用,因为它们可以在大量值中快速查找值.他们使用的两个例子是Python setdict.在a中list,如果要检查列表中是否有值if x in values:,则Python需要遍历整个列表并x与列表中的每个值进行比较values.这可能需要很长时间list.在a中set,Python会跟踪每个哈希值,当您键入时if x in values:,Python将获取哈希值x,在内部结构中查找,然后仅x与具有相同哈希值的值进行比较x.

相同的方法用于字典查找.这使得查找setdict速度非常快,而在查找list缓慢.它还意味着您可以在a中使用不可清除的对象list,但不能在a set或中作为键dict.不可清除对象的典型示例是任何可变的对象,这意味着您可以更改其值.如果你有一个可变对象它不应该是可散列的,因为它的散列会在其生命周期内发生变化,这会引起很多混乱,因为一个对象可能最终在字典中的错误散列值下.

请注意,对于一次Python运行,值的哈希值只需要相同.在Python 3.3中,它们实际上会针对每次新的Python运行进行更改:

$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
1849024199686380661
>>> 
$ /opt/python33/bin/python3
Python 3.3.2 (default, Jun 17 2013, 17:49:21) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> hash("foo")
-7416743951976404299
Run Code Online (Sandbox Code Playgroud)

这使得更难以猜测某个字符串将具有哪个哈希值,这是Web应用程序等的重要安全功能.

因此,不应永久存储散列值.如果您需要以永久方式使用哈希值,您可以查看更"严重"的哈希类型,加密哈希函数,可用于制作可验证的文件校验和等.

  • 关于潜在的哈希冲突:`hash(-1)== hash(-2)`(runnin Python 2.7) (8认同)
  • `hash(-1) == hash(-2)` 今天仍然存在。幸运的是,它不会对字典和集合查找产生不利影响。除了“-1”之外,所有其他整数“i”都解析为“hash(i)”自身。 (5认同)
  • 我正在运行Python 3.6.1,并且存在冲突。 (2认同)

dno*_*zay 27

TL; DR:

请参考词汇表:hash()用作比较对象的快捷方式,如果可以将对象与其他对象进行比较,则认为该对象是可清除的.这就是我们使用的原因hash().它还用于访问dict在CPythonset中实现为可调整大小的哈希表的元素.

技术考虑

  • 通常比较对象(可能涉及多个级别的递归)是昂贵的.
  • 优选地,该hash()功能是一个数量级(或几个)更便宜的.
  • 比较两个哈希比比较两个对象更容易,这就是快捷方式所在的位置.

如果你读到如何实现字典,他们会使用哈希表,这意味着从对象中获取一个键是用于检索词典中的对象的基石O(1).然而,这非常依赖于您的哈希函数来抵抗冲突.实际上,在字典中获取项目最坏情况O(n).

在这方面,可变对象通常是不可清除的.hashable属性意味着您可以将对象用作键.如果哈希值用作键并且同一对象的内容发生更改,那么哈希函数应该返回什么?是同一把钥匙还是另一把钥匙?这取决于您如何定义哈希函数.

通过实例学习:

想象一下,我们有这个课程:

>>> class Person(object):
...     def __init__(self, name, ssn, address):
...         self.name = name
...         self.ssn = ssn
...         self.address = address
...     def __hash__(self):
...         return hash(self.ssn)
...     def __eq__(self, other):
...         return self.ssn == other.ssn
... 
Run Code Online (Sandbox Code Playgroud)

请注意:这都是基于SSN永远不会改变个人的假设(甚至不知道从权威来源实际验证该事实的位置).

我们有鲍勃:

>>> bob = Person('bob', '1111-222-333', None)
Run Code Online (Sandbox Code Playgroud)

鲍勃去看法官改名:

>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
Run Code Online (Sandbox Code Playgroud)

这就是我们所知道的:

>>> bob == jim
True
Run Code Online (Sandbox Code Playgroud)

但这些是分配了不同内存的两个不同对象,就像同一个人的两个不同记录一样:

>>> bob is jim
False
Run Code Online (Sandbox Code Playgroud)

现在,hash()很方便:

>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
Run Code Online (Sandbox Code Playgroud)

你猜怎么着:

>>> dmv_appointments[jim] #?
'tomorrow'
Run Code Online (Sandbox Code Playgroud)

从两个不同的记录中,您可以访问相同的信息.现在试试这个:

>>> dmv_appointments[hash(jim)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in __eq__
AttributeError: 'int' object has no attribute 'ssn'
>>> hash(jim) == hash(hash(jim))
True
Run Code Online (Sandbox Code Playgroud)

刚刚发生了什么?那是一次碰撞.因为hash(jim) == hash(hash(jim))它们都是整数btw,我们需要比较__getitem__所有碰撞项目的输入.内置版int没有ssn属性,所以它会跳转.

>>> del Person.__eq__
>>> dmv_appointments[bob]
'tomorrow'
>>> dmv_appointments[jim]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: <__main__.Person object at 0x7f611bd37110>
Run Code Online (Sandbox Code Playgroud)

在最后一个例子中,我表明即使碰撞,执行比较,对象也不再相等,这意味着它成功地引发了一个KeyError.

  • 有人可以详细说明上面例子中使用`__eq__`.当它试图将它收到的密钥与它拥有的所有密钥进行比较时,它是否被字典调用?这样,在上一个例子中,通过`del`的`__eq__`方法,字典没有什么可以用来确定它用它拥有的密钥所获得的密钥的等价性? (2认同)