Python hash() 函数

Python hash() 语法

hash() 是 Python 的一个内置函数,它用于获取一个对象对应的哈希值(散列值)。哈希值是一个整数,具有以下特点。

  • 对于同一对象来说,在程序的不同执行期间,哈希值保持不变。
  • 对于不同对象来说,哈希值可能相同,但概率非常小。
  • 在不同 Python 进程或不同平台中,相同对象可能返回不同的哈希值(尤其对字符串类型)。

语法:

hash(object)

说明:

hash() 函数接收单个参数。

  • object(必选):它必须是一个不可变对象(可哈希对象),比如数字、元组、字符串等。

注意:

  • 并不是所有对象都可以被哈希,只有不可变对象才可以。可变对象(如 列表、字典集合等)是不能被哈希的,因为它们的值可以改变。如果尝试对可变对象进行哈希操作,则会抛出 TypeError 异常。
  • 使用 hash() 处理的对象是不可逆的,它会导致信息丢失。
  • 不同的 Python 解释器或在不同的运行环境下,相同对象的哈希值可能不同。因此,我们不应该依赖于哈希值的具体数值,而只应依赖其作为唯一标识的能力。

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() 结果可能不一致。在需要哈希值跨进程或跨运行保持一致的特定场景,才需要考虑禁用它。

上一篇: id()

下一篇: breakpoint()

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号