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.key和self.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.key和self.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的数据结构和算法。
极客教程