我需要将大量对象存储在内存中以便在Python中进行处理。具体来说,我正在尝试从大量对象中删除重复项。如果对象中的某个实例变量相等,我想将两个对象视为“相等”。所以,我认为最简单的方法是将我的所有对象插入到一个集合中,并覆盖__hash__
方法,以便它散列我关注的实例变量。
因此,作为测试我尝试了以下内容:
class Person:
def __init__(self, n, a):
self.name = n
self.age = a
def __hash__(self):
return hash(self.name)
def __str__(self):
return "{0}:{1}".format(self.name, self.age)
myset = set()
myset.add(Person("foo", 10))
myset.add(Person("bar", 20))
myset.add(Person("baz", 30))
myset.add(Person("foo", 1000)) # try adding a duplicate
for p in myset: print(p)
在这里,我定义了一个Person
类,并且Person
具有相同name
变量的任何两个实例都是相同的,无论其他任何实例变量的值如何。不幸的是,这输出:
baz:30
foo:10
bar:20
foo:1000
请注意,foo
出现两次,因此该程序未发现重复项。然而,hash(Person("foo", 10)) == hash(Person("foo", 1000))
这个词是True
。那么为什么这不能正确检测重复的Person
个对象呢?
答案 0 :(得分:11)
你忘了也 define __eq__()
。
如果某个类未定义
__cmp__()
或__eq__()
方法,则不应定义__hash__()
操作;如果它定义__cmp__()
或__eq__()
但不定义__hash__()
,则其实例将无法在散列集合中使用。如果一个类定义了可变对象并实现了__cmp__()
或__eq__()
方法,那么它不应该实现__hash__()
,因为hashable collection实现要求对象的哈希值是不可变的(如果对象的哈希值)更改,它将在错误的哈希桶中。)
答案 1 :(得分:4)
显然,一组必须处理哈希冲突。如果两个对象的散列匹配,则该集将使用==
运算符对它们进行比较,以确保它们真的相等。在您的情况下,如果两个对象是同一个对象(用户定义类的标准实现),则只会产生True
。
长话短说:同时定义__eq__()
以使其有效。
答案 2 :(得分:2)
散列函数不足以区分您必须实现比较函数的对象(即__eq__
)。
答案 3 :(得分:1)
哈希函数有效地说“A可能等于B”或“A不等于B(肯定)”。
如果它说“可能等于”那么必须检查平等以确保,这就是为什么你还需要实现__eq__
。
尽管如此,定义__hash__
可以通过使“A不等于B(肯定)”O(1)
操作来显着加快速度。
然而,哈希函数必须始终遵循“哈希规则”:
例如,您可以按def __hash__(self): return 1
对所有内容进行哈希处理。这仍然是正确的,但是效率很低,因为每次都必须检查__eq__
,如果你有复杂的大型数据结构(例如大型列表,字典等),这可能是一个漫长的过程。
请注意,您在技术上遵循“哈希规则”,通过忽略实施def __hash__(self): return self.name
中的年龄来执行此操作。如果鲍勃是一个20岁的人,鲍勃是另一个30岁的人,他们是不同的人(可能除非这是某种随时跟踪他们的年龄段跟踪计划),那么它们将散列为相同的值,并且必须与__eq__
进行比较。这很好,但我会像这样实现它:
def __hash__(self):
return hash( (self.name, self.age) )
请注意您的方式仍然正确。然而,在hash( (self.name, self.age) )
和Person("Bob", age=20)
实际上是同一个人的世界中使用Person("Bob", age=30)
会产生编码错误,因为哈希函数会说它们在等于功能不会(但可以忽略)。
答案 4 :(得分:0)
您还需要__ eq __()方法。