Python 如何正确使对象具有可哈希性

Python 如何正确使对象具有可哈希性

在本文中,我们将介绍如何在Python中正确使对象具有可哈希性。可哈希性是指对象能够被用作字典中的键或集合中的成员。在Python中,不可变对象默认是可哈希的,而可变对象则默认是不可哈希的。然而,有时我们希望能够自定义一个对象的哈希行为。下面我们将探讨几种方法来实现这个目标。

阅读更多:Python 教程

重写 hash 方法

要使对象具有可哈希性,我们可以重写对象的__hash__方法。__hash__方法是Python内建的一个方法,用于计算对象的哈希值。下面是一个示例:

class MyClass:
    def __init__(self, key, value):
        self.key = key
        self.value = value

    def __hash__(self):
        return hash((self.key, self.value))

obj1 = MyClass(1, 'one')
obj2 = MyClass(2, 'two')

print(hash(obj1))  # 输出:8731490573967612268
print(hash(obj2))  # 输出:5501812825858369905

在上面的示例中,我们重写了MyClass类的__hash__方法,并使用self.keyself.value作为哈希值的计算基础。通过这种方式,我们可以获得一种自定义的哈希行为。

需要注意的是,如果一个对象被声明为可哈希的,那么它的哈希值必须保持不变。换句话说,只有不可变的对象才能正确地使用哈希功能。如果对象是可变的,我们需要小心处理。下面是一个示例:

class MyMutableClass:
    def __init__(self, key, value):
        self.key = key
        self.value = value

    def __hash__(self):
        return hash((self.key, self.value))

obj = MyMutableClass(1, 'one')
print(hash(obj))  # 抛出 TypeError:unhashable type: 'MyMutableClass'

在上面的示例中,尽管我们重写了MyMutableClass类的__hash__方法,但由于MyMutableClass对象是可变的,因此它被认为是不可哈希的。

重写 eq 方法

除了重写__hash__方法外,我们还需要重写对象的__eq__方法。__eq__方法用于检查两个对象是否相等。在哈希表中,两个对象如果相等,则它们的哈希值也必须相等。下面是一个示例:

class MyClass:
    def __init__(self, key, value):
        self.key = key
        self.value = value

    def __hash__(self):
        return hash((self.key, self.value))

    def __eq__(self, other):
        if isinstance(other, type(self)):
            return self.key == other.key and self.value == other.value
        return False

obj1 = MyClass(1, 'one')
obj2 = MyClass(1, 'one')

print(obj1 == obj2)  # 输出:True
print(hash(obj1) == hash(obj2))  # 输出:True

在上面的示例中,我们重写了MyClass类的__eq__方法,并根据self.keyself.value的值来判断两个对象是否相等。当两个对象相等时,它们的哈希值也相等。

使用 functools 模块的 @total_ordering 装饰器

如果我们只重写了__eq__方法,那么对象只能进行相等性比较,无法进行大小比较。如果我们希望对象支持大小比较,可以使用functools模块中的@total_ordering装饰器。下面是一个示例:

from functools import total_ordering

@total_ordering
class MyClass:
    def __init__(self, key, value):
        self.key = key
        self.value = value

    def __hash__(self):
        return hash((self.key, self.value))

    def __eq__(self, other):
        if isinstance(other, type(self)):
            return self.key == other.key and self.value == other.value
        return NotImplemented

    def __lt__(self, other):
        if isinstance(other, type(self)):
            return (self.key, self.value) < (other.key, other.value)
        return NotImplemented

obj1 = MyClass(1, 'one')
obj2 = MyClass(2, 'two')

print(obj1 < obj2)  # 输出:True
print(obj1 > obj2)  # 输出:False
print(obj1 == obj2)  # 输出:False
print(obj1 <= obj2)  # 输出:True
print(obj1 >= obj2)  # 输出:False

在上面的示例中,我们使用@total_ordering装饰器,然后只重写了__eq____lt__方法。@total_ordering装饰器会自动帮我们生成其他比较方法。

总结

通过重写__hash____eq__方法,我们可以在Python中实现自定义的哈希行为。而使用@total_ordering装饰器,则可以让对象支持大小比较。这样,我们就能够准确地将对象用作字典的键或集合的成员,从而更好地利用Python的数据结构和算法。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程