Python hash() 语法
hash() 是 Python 的一个内置函数,它用于获取一个对象对应的哈希值(散列值)。哈希值是一个整数,具有以下特点。
- 对于同一对象来说,在程序的不同执行期间,哈希值保持不变。
- 对于不同对象来说,哈希值可能相同,但概率非常小。
- 在不同 Python 进程或不同平台中,相同对象可能返回不同的哈希值(尤其对字符串类型)。
语法:
hash(object)说明:
hash() 函数接收单个参数。
object(必选):它必须是一个不可变对象(可哈希对象),比如数字、元组、字符串等。
注意:
Python hash() 摘要
| 使用频率 | 中 |
|---|---|
| 官方文档 | 查看 |
| 相关函数 | id() |
Python hash() 示例
接下来,我们通过几个简单的例子来讲解一下 Python hash() 函数是如何使用的。
示例 1:hash() 基本用法
print(hash(666))
print(hash(3.14))
print(hash('Python'))运行结果如下。
666
322818021289917443
-8217463795919859747(随机值)分析:
整数的哈希值通常等于自身,浮点数和字符串的哈希值依赖具体实现,且字符串哈希值在不同 Python 进程中可能变化。
示例 2:hash() 参数是不可变对象
tup = ('l', 'v', 'y', 'e')
print(hash(tup))运行结果如下。
-8261658847670450095分析:
hash() 参数只能是不可变对象,元组是不可变对象,但像列表、字典等就不是了。
示例 3:hash() 参数是可变对象
colors = ['red', 'green', 'blue']
print(hash(colors))运行结果如下。
(报错) TypeError: unhashable type: 'list'分析:
如果 hash() 参数是可变对象(如列表、字典等),则会直接报错。
深入了解 hash()
hash() 函数在 Python 的内部机制中扮演着重要角色,尤其是在处理集合(set)和字典(dict)的键时。深入理解它的工作原理和相关概念,有助于我们编写更健壮、更高效的代码。
1. 自定义类如何实现哈希
自定义对象可以通过实现 __hash__() 和 __eq__() 这两个魔法方法,来决定它们如何参与哈希操作以及如何判断相等。当一个对象实现了 __hash__() 方法时,hash() 函数在被调用时就会返回该方法定义的结果。
示例 4:自定义 __hash__() 方法
class MyObject:
def __init__(self, value, name=''):
self.value = value
self.name = name
# 定义相等性:只要 value 相同就认为是相等对象
def __eq__(self, other):
if not isinstance(other, MyObject):
return NotImplemented
return self.value == other.value
# 定义哈希值:基于 value 来计算哈希
# 这样可以保证 value 相同的对象有相同的哈希值
def __hash__(self):
return hash(self.value)
# 创建 MyObject 实例
obj_a = MyObject(123, 'Item A')
obj_b = MyObject(123, 'Item B') # 与 obj_a 的 value 相同,但 name 不同
obj_c = MyObject(456, 'Item C')
print(f'obj_a 的哈希值: {hash(obj_a)}')
print(f'obj_b 的哈希值: {hash(obj_b)}')
print(f'obj_c 的哈希值: {hash(obj_c)}')
print(hash(obj_a) == hash(obj_b))
print(hash(obj_a) == hash(obj_c))
# 将自定义对象作为字典的键或集合的元素
my_dict = {obj_a: '对象A', obj_c: '对象C'}
print(my_dict)
# 由于 obj_b 与 obj_a 相等且哈希值相同,它会覆盖 obj_a 对应的键值
my_dict[obj_b] = 'obj_bbb'
print(my_dict) # 注意这里 obj_a 的键被覆盖了
my_set = {obj_a, obj_b, obj_c}
print(list(my_set)) # obj_a 和 obj_b 被认为是同一个元素运行结果如下。
obj_a 的哈希值: 123
obj_b 的哈希值: 123
obj_c 的哈希值: 456
True
False
{<__main__.MyObject object at 0x7f9a28646900>: '对象A', <__main__.MyObject object at 0x7f9a284c4cd0>: '对象C'}
{<__main__.MyObject object at 0x7f9a28646900>: 'obj_bbb', <__main__.MyObject object at 0x7f9a284c4cd0>: '对象C'}
[<__main__.MyObject object at 0x7f9a284c4cd0>, <__main__.MyObject object at 0x7f9a28646900>]分析:
在这个例子中,我们在自定义类中实现 __hash__() 方法,并确保与 __eq__() 方法保持一致性。当 obj_a 和 obj_b 因其 value 相同而被认为是相等时,它们的哈希值也相同,这使得它们在字典和集合中表现为同一个 “键” 或 “元素”。
2. 哈希与对象相等性
Python 中关于哈希和对象相等性有一个基本且强制性的原则:
- 如果两个对象相等(即 a == b 为 True),那么它们的哈希值必须相等(即 hash(a) == hash(b) 必须为 True)。
- 反之则不然:如果两个对象的哈希值相同,它们不一定相等(这被称为 “哈希冲突”)。但是,如果它们的哈希值不同,那么它们必然不相等。
3. 哈希随机化
从 Python 3.3 版本开始,Python 默认对字符串(str)和字节串(bytes)的哈希值引入了 “哈希随机化”(hash randomization)。这意味着每次启动 Python 解释器时,这些对象的哈希值计算方式都会略有不同,从而导致哈希值每次运行都可能不一样。
这种机制的主要目的是增强安全性,防止某些 “哈希冲突攻击”(hash collision attacks)。在这些攻击中,恶意用户可以通过发送大量具有相同哈希值的输入,来降低哈希表的性能,甚至导致拒绝服务。
我们可以通过设置 PYTHONHASHSEED 环境变量为固定的整数值(例如 0)来禁用哈希随机化。
# Linux/macOS
PYTHONHASHSEED=0 python your_script.py
# Windows (Command Prompt)
set PYTHONHASHSEED=0
python your_script.py
# Windows (PowerShell)
$env:PYTHONHASHSEED='0'
python your_script.py哈希随机化是一个底层的安全特性,通常开发者不需要关心其具体实现,但了解它的存在可以帮助解释为什么在不同次运行中,字符串的 hash() 结果可能不一致。在需要哈希值跨进程或跨运行保持一致的特定场景,才需要考虑禁用它。
