说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 的内置函数 hash() 可以返回对象的哈希值。哈希值又称散列值,它是一个固定大小的整数,用于标识对象的特定值。hash() 又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。
一些快速的示例:
hash('你好')
# 2439288928012809752
hash(123)
# 123
hash(123.0)
# 123
hash(range(2))
# 7853416581674910768
hash((1, 2))
# -3550055125485641917
hash([1, 2])
# TypeError: unhashable type: 'list'
返回该对象的哈希值(如果它有的话)。哈希值是整数。它们在字典查找元素时用来快速比较字典的键。
相同大小的数字变量有相同的哈希值(即使它们类型不同,如 1 和 1.0)。
所有对象的 hash 值都不是 -1。咱们知道一定长度的数字的 hash 是它自己,如 hash(2) 是 2,hash(100000) 是 100000,但 hash(-1) 是 -2。
散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums,或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到。
如今,散列算法也被用来加密存在数据库中的密码(password)字符串,由于散列算法所计算出来的散列值(Hash Value)具有不可逆(无法逆向演算回原本的数值)的性质,因此可有效的保护密码。
所有散列函数都有如下一个基本特性:
如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。
这个特性是散列函数具有确定性的结果,具有这种性质的散列函数称为单向散列函数。但另一方面,散列函数的输入和输出不是唯一对应关系的,如果两个散列值相同,两个输入值很可能是相同的,但也可能不同,这种情况称为“散列碰撞(collision)”,这通常是两个不同长度的输入值,刻意计算出相同的输出值。输入一些数据计算出散列值,然后部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。
其他说明:
常用 hash 算法的介绍:
(1)MD4
MD4(RFC 1320)是 MIT 的Ronald L. Rivest在 1990 年设计的,MD 是 Message Digest(消息摘要) 的缩写。它适用在32位字长的处理器上用高速软件实现——它是基于 32位操作数的位操作来实现的。
(2)MD5
MD5(RFC 1321)是 Rivest 于1991年对MD4的改进版本。它对输入仍以512位分组,其输出是4个32位字的级联,与 MD4 相同。MD5比MD4来得复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好。
(3)SHA-1及其他
SHA1是由NIST NSA设计为同DSA一起使用的,它对长度小于264的输入,产生长度为160bit的散列值,因此抗穷举(brute-force)性更好。SHA-1 设计时基于和MD4相同原理,并且模仿了该算法。
Python 的 hash(object) 返回传入对象的哈希值(如果它有的话),哈希值是整数。它们在字典查找元素时用来快速比较字典的键。相同大小的数字变量有相同的哈希值(即使它们类型不同,如 1 和 1.0)。
Python 的不可变对象才有 hash 值,可变对象没有 hash 值,我们称它为 不可哈希,如上例中的列表。因此,hash() 可以应用于数字、字符串和对象,不能直接应用于 list、set、dictionary。
一些情况下,hash() 操作每次得到的值不一样,这是为什么?
集合(set)的元素、字典(dict)的 key 必须是可哈希的,它保证了在同一个解释器进程里相同字符串 hash 一致,在不同进程中 字符串的 hash 可能不一样。
hash() 对对象使用时,所得的结果不仅和对象的内容有关,还和对象的 id(),也就是内存地址有关。hash() 本身就不是专为字符串设计的,只是为了在程序运行过程中区别不同对象。
所以真需要做可重现可跨进程保持一致性的 hash,请用 hashlib。
如果对象实现了自己的 __hash__()
方法,hash() 根据机器的字长来截断返回值。自己要实现 hash 功能,可以在对象中来重写 __hash__()
来自定义 hash 值。
自己定义的 __hash__()
要返回一个整数,必须同时实现 __eq__()
和 __hash__()
,下面是正确是正确的的例子:
class Person:
def __init__(self, age, name):
self.age = age
self.name = name
def __eq__(self, other):
return self.age == other.age and self.name == other.name
def __hash__(self):
print('The hash is:')
return hash((self.age, self.name))
person = Person(23, 'Adam')
hash(person)
# -1253368434537217793
如无必要,对象不必自己实现 __eq__()
方法,因为它是默认为所有对象创建的。
__eq__() | __hash__() | Description |
---|---|---|
Defined (by default) | Defined (by default) | If left as is, all objects compare unequal (except themselves) |
(If mutable) Defined | Should not be defined | Implementation of hashable collection requires key's hash value be immutable |
Not defined | Should not be defined | If __eq__() isn't defined, __hash__() should not be defined. |
Defined | Not defined | Class instances will not be usable as hashable collection. __hash__() implicity set to None . Raises TypeError exception if tried to retrieve the hash. |
Defined | Retain from Parent | __hash__ = <ParentClass>.__hash__ |
Defined | Doesn't want to hash | __hash__ = None . Raises TypeError exception if tried to retrieve the hash. |
常见用途有:
密码存储常采用 MD5 算法,用户输入明文,数据库存入 MD5 后的固定长度字符串,因此任何人都不知道用户的密码,在下次输入时用户用明文与 MD5 值对比验证。
这样的好处是一旦数据库发生泄露或者被人意外看见,也无法知识用户的密码。
用来快速检验数据丢包的问题,在数据发送前把数据内容进行 Hash 计算并把这个值一并传给接收方,同时在接受方同样对数据内容进行Hash计算,如果两边Hash值不一致,就能知道数据在传输过程中发生了错误。
在使用文件或者软件时,将收到的文件 hash 再与作者、发送者提供 hash 值对比,就能知道文件是否有错误、篡改、丢失等情况。
等等。
更新时间:2022-06-14 20:31:43 标签:python hash 哈希