Python hash()函数
Python hash()是内置函数之一。
今天,我们将研究hash()函数的用法以及如何为自定义对象覆盖它。
什么是哈希?
用最简单的术语来说,哈希是标识特定值的固定大小的整数。
请注意,这是最简单的解释。
让我们指出固定哈希值的含义:
相同的数据将具有相同的哈希值。
即使原始数据稍有变化,也可能导致完全不同的哈希值。
从哈希函数获得哈希,哈希函数负责将给定信息转换为编码哈希。
显然,对象的数量可以远远大于哈希值的数量,因此,两个对象可以哈希为相同的哈希值。
这称为哈希冲突。
这意味着,如果两个对象具有相同的哈希码,则它们不一定具有相同的值。
什么是Python哈希函数?
我们可以深入介绍有关散列的细节,但是在此处值得一提的是有关制作GOOD Hash函数的重要一点:
一个好的哈希函数是导致冲突次数最少的函数,这意味着,没有2组信息应该具有相同的哈希值。
除了上述定义之外,就空间和内存复杂性而言,对象的哈希值应该便宜。
在完成字典键的比较时,最常用哈希码。
当对特定键进行字典查找时,将比较字典键的哈希码。
比较散列比比较完整键值要快得多,因为散列函数将每个字典键映射到的整数集远小于对象集本身。
另外,请注意,如果两个数值可以比较相等,则即使它们属于不同的数据类型(例如1和1.0),它们也将具有相同的哈希值。
Python hash()字符串
让我们开始构建简单的示例和场景,其中的hash()函数可能会非常有帮助。
在此示例中,我们将简单地获取String的哈希值。
name = "Shubham" hash1 = hash(name) hash2 = hash(name) print("Hash 1: %s" % hash1) print("Hash 2: %s" % hash2)
运行此脚本时,将获得以下结果:
这是一个重要的收获。
如果再次运行相同的脚本,则哈希值将发生如下变化:
因此,哈希的寿命仅适用于程序范围,并且可以在程序结束后立即更改。
Python哈希值略有变化
其中我们将看到数据的微小变化如何改变哈希值。
它会完全改变还是改变一点?更好的方法是通过脚本找出答案!
name1 = "Shubham" name2 = "Shubham!" hash1 = hash(name1) hash2 = hash(name2) print("Hash 1: %s" % hash1) print("Hash 2: %s" % hash2)
现在运行此脚本:
看看当原始数据中只有一个字符改变时,哈希如何完全改变?这使得哈希值完全不可预测!
如何为自定义对象定义hash()函数?
在内部,hash()
函数通过覆盖__hash __()
函数来工作。
值得注意的是,并非每个对象都是可哈希的(可变集合不是哈希的)。
我们还可以为我们的自定义类定义此函数。
实际上,这就是我们现在要做的。
在此之前,让我们指出一些要点:
对于可变集合,不应执行可哈希实现,因为集合的键对于哈希而言应是不可变的。
我们不必为所有对象都定义一个自定义的
__eq __()
函数实现。
现在,让我们定义一个对象并覆盖__hash __()
函数:
class Student: 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): return hash((self.age, self.name)) student = Student(23, 'Shubham') print("The hash is: %d" % hash(student))
该程序实际上描述了我们如何覆盖__eq __()
和__hash __()
函数。
这样,我们实际上可以定义自己的逻辑来比较任何对象。
为什么可变的对象不能散列?
众所周知,只能对不可变的对象进行哈希处理。
不允许对可变对象进行哈希处理的限制大大简化了哈希表。
让我们了解如何。
如果允许对可变对象进行哈希处理,则每次对象值更新时,我们都需要更新哈希表。
这意味着我们将不得不将对象移动到完全不同的存储桶中。
这是要执行的非常昂贵的操作。
在Python中,我们有两个使用哈希表的对象,字典和集合:
词典是一个哈希表,称为关联数组。
在字典中,仅对键进行哈希处理,而不对值进行哈希处理。
这就是为什么字典键也应该是不可变对象的原因,而值可以是任何值,甚至是可变的列表。一组包含可哈希的唯一对象。
如果我们有不可散列的项目,则不能使用set,而必须使用list。