# Python 哈希教程

> 原文： [http://zetcode.com/python/hashing/](http://zetcode.com/python/hashing/)

Python 哈希教程解释了 Python 中的哈希概念。 我们介绍了哈希表和 Python 可哈希对象。

## 哈希表

哈希表用于以许多常见的编程语言（例如 C++ ，Java 和 Python）实现映射和设置数据结构。 Python 将哈希表用于字典和集合。 哈希表是键值对的无序集合，其中每个键都是唯一的。 哈希表提供了有效的查找，插入和删除操作的组合。 这些是数组和链接列表的最佳属性。

## 散列

哈希是使用算法将任意大小的数据映射到固定长度的过程。 这称为哈希值。 散列用于创建高性能，直接访问的数据结构，在该结构中要快速存储和访问大量数据。 哈希值使用哈希函数计算。

## Python 可哈希对象

如果对象的哈希值在其生命周期内从未发生变化，则该对象是可哈希的。 （在多次调用 Python 程序期间，它可以具有不同的值。）可哈希对象需要`__hash__()`方法。 为了执行比较，散列需要一种`__eq__()`方法。

> **注意**：比较相等的可哈希对象必须具有相同的哈希值。

散列性使对象可用作字典键和集成员，因为这些数据结构在内部使用散列值。 Python 不可变的内置对象是可哈希的； 可变容器（例如列表或字典）不是。 默认情况下，作为用户定义类实例的对象是可哈希的。 它们都比较不相等（除了它们本身），并且它们的哈希值是从`id()`派生的。

> **注意**：如果一个类没有定义一个`__eq __()`方法，它也不应该定义一个`__hash __()`操作。 如果它定义了`__eq __()`而不是`__hash __()`，则其实例将不能用作可散列集合中的项目。

## Python `hash()`函数

`hash()`函数返回对象的哈希值（如果有的话）。 哈希值是整数。 它们用于在字典查找期间快速比较字典关键字。 对象可以实现`__hash__()`方法。

## Python 不可变内置函数可哈希化

Python 不变的内置函数（例如整数，字符串或元组）是可哈希的。

`builtin_hashables.py`

```py
#!/usr/bin/env python3

val = 100

print(val.__hash__())
print("falcon".__hash__())
print((1,).__hash__())

```

该示例显示三个哈希值的值：整数，字符串和元组。

## Python 自定义可哈希对象示例 I

Python 自定义对象默认情况下是可哈希的。 他们的哈希值是从其 ID 派生的。

`custom_object.py`

```py
#!/usr/bin/env python3

class User:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

print('hash of user 1')
print(hash(u1))

print('hash of user 2')
print(hash(u2))

if (u1 == u2):
    print('same user')
else:
    print('different users')

```

在示例中，我们有`User`的两个实例。

```py
u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

```

我们有两个具有相同数据的实例。

```py
print('hash of user 1')
print(hash(u1))

```

`hash()`函数返回对象的哈希值。 默认实现是从对象的 ID 派生的。

```py
$ python custom_object.py
hash of user 1
-9223371894419573195
hash of user 2
142435202673
different users

```

即使用户详细信息相同，但比较仍会产生不同的对象。 为了更改它，我们需要实现`__eq__()`方法。

## Python 自定义可哈希对象示例 II

在第二个示例中，我们实现了自定义`__eq__()`方法。

`custom_object2.py`

```py
#!/usr/bin/env python3

class User:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

    def __eq__(self, other):

        return self.name == other.name \
            and self.occupation == other.occupation

    def __str__(self):
        return f'{self.name} {self.occupation}'

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

# users = {u1, u2}
# print(len(users))

```

现在比较返回给我们的预期输出； 但是，我们不能将对象插入 Python 集中； 这将导致`TypeError: unhashable type: 'User'`。 为了更改此设置，我们实现了`__hash__()`方法。

## Python 自定义可哈希对象示例 III

在第三个示例中，我们实现了`__eq__()`和`__hash__()`方法。

`custom_object3.py`

```py
#!/usr/bin/env python3

class User:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

    def __eq__(self, other):

        return self.name == other.name \
            and self.occupation == other.occupation

    def __hash__(self):
        return hash((self.name, self.occupation))

    def __str__(self):
        return f'{self.name} {self.occupation}'

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

users = {u1, u2}

print(len(users))

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

print('------------------------------------')

u1.occupation = 'programmer'

users = {u1, u2}

print(len(users))

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

```

该示例比较了具有`__eq__()`和`__hash__()`方法的自定义实现的两个对象。 可以将这些对象插入 Python 集中，当以后更改属性时，我们将获得预期的输出。

```py
def __hash__(self):
    return hash((self.name, self.occupation))

```

`__hash__()`函数的实现从属性元组返回使用`hash()`函数计算的哈希值。

```py
$ python custom_object3.py
1
same user
John Doe gardener == John Doe gardener
------------------------------------
2
different users

```

这是输出。

## Python `@dataclass`装饰器

从 Python 3.7 开始，我们有了`dataclass`装饰器，它会自动生成一些样板代码。

数据类装饰器的冻结参数（默认为`False`）。 如果指定，则字段将被冻结（即只读）。 如果`eq`设置为`True`（默认情况下），则将实现`__hash__()`方法，并且对象实例将是可哈希的。

`decorator.py`

```py
#!/usr/bin/env python3

from dataclasses import dataclass

@dataclass(frozen=True)
class User:

    name: str
    occupation: str

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

users = {u1, u2}
print(len(users))

```

该示例使用`@dataclass`装饰器。

```py
$ python decorator.py
same user
User(name='John Doe', occupation='gardener') == User(name='John Doe', occupation='gardener')
1

```

这是输出。

在本教程中，我们介绍了 Python 中的哈希。

您可能也对以下相关教程感兴趣： [Python 教程](/lang/python/)， [Python 列表推导](/articles/pythonlistcomprehensions/)或列表[所有 Python 教程](/all/#python)。