为什么在我的Python集中添加了一个重复的对象,而它不应该';不会吧?

为什么在我的Python集中添加了一个重复的对象,而它不应该';不会吧?,python,set,Python,Set,我对此有一个类定义: class Question: title = "" answer = "" def __init__(self, title, answer): self.title = title self.answer = answer def __eq__(self, other): return self.title == other.title and self.answer

我对此有一个类定义:

class Question:
    title = ""
    answer = ""
    def __init__(self, title, answer):
            self.title = title
            self.answer = answer
    def __eq__(self, other):
            return self.title == other.title and self.answer == other.answer
    def __hash__(self):
        return hash(repr(self))
我试图将这些对象中的许多添加到一个集合中,但前提是该对象与集合中已有的任何其他对象的属性都不相同:

        questionset = set()
        q = Question(questionparts[0] + questionparts[1], questionparts[2])
        if q not in questionset:
            questionset.add(q)
如果我有两个问题,每个问题都有相同的属性值,我希望只有一个问题被添加到我的集合中,而我的集合的长度是2

我做错了什么?如果我记录每个问题对象,我可以确认这些项目具有相同的属性值

您的哈希函数非常无效。Python要求您的
\uuuuuuuuuuuuuuuuuuuuuuu散列
函数为两个被认为相等的对象返回相同的值,但您的函数不返回相同的值。从:

唯一需要的属性是比较相等的对象具有相同的哈希值

repr(self)
返回使用对象id的默认表示形式。它基本上会根据对象标识返回不同的哈希。你不妨这样做:

return hash(id(self))
这不是一个好的散列,因为所有实例的值都不同。因此,
hash()
值无法满足所需的属性:

>>> a = Question('foo', 'bar')
>>> b = Question('foo', 'bar')
>>> a == b
True
>>> hash(a) == hash(b)
False
您需要散列您的属性:

return hash(self.title + self.answer)
现在,哈希值基于通知相等的相同值。

for
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

唯一需要的属性是比较相等的对象具有相同的哈希值

您的类不符合此要求。由于不重写
\uuu repr\uuu
repr(self)
将使用默认行为,这将为您提供类似
的内容。这意味着每个
问题
实例将具有不同的哈希值——即使比较相等的实例也是如此


你需要重写你的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu散列
以与你的
\uuuuuuuuuuuuuuuuuueq
兼容。如果要比较标题/答案是否相等,最简单的解决方案是同时对对象的标题和答案进行散列,例如通过返回
散列((self.title,self.answer))
对对象进行散列,而不是对值进行散列。尝试散列
self.title+self.answer
而不是像Martijn Pieters和BrenBarn提到的那样散列
self

,因为默认的
\urepr\uuu()方法的行为,所以您的
\uuuuuhash\uuuuuu()
方法无法工作。他们的答案显示了
\uuuuu hash\uuuuu()
的替代实现;下面的代码实现了一个简单但有用的
\uu repr\uu()

我并不是说我的方法比Martijn或BrenBarn的方法好。我只是想说明,一旦您的类有了一个合适的
\uuu repr\uu()
方法,您原来的
\uu hash\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu()
方法就会起作用

FWIW,通常最好为类实现
\uuuu repr\uuu()
方法。即使您在最终的生产代码中实际上不需要该方法,它也有助于在开发代码时进行测试

我在Python2.6.6上运行它,因此从未来导入的
和从对象的显式继承
;您不需要在Python3中执行这些操作,但代码仍应按原样在Python3上正常运行

#!/usr/bin/env python

from __future__ import print_function

class Question(object):
    def __init__(self, title, answer):
        self.title = title
        self.answer = answer

    def __repr__(self):
        return 'Q({s.title!r}, {s.answer!r})'.format(s=self)

    def __eq__(self, other):
        return self.title == other.title and self.answer == other.answer

    def __hash__(self):
        return hash(repr(self))

questionset = set()

q1 = Question('2+2', '4')
q2 = Question('2+2', '4')
q3 = Question('2*2', '4')

print(q1, q2, q3)
print(q1 == q2, q1 == q3)

questionset.update((q1, q2, q3))
print(questionset)

questionset.add(Question('1+3', '4'))
questionset.add(Question('2+3', '5'))
questionset.add(Question('2*3', '6'))
questionset.add(Question('1+3', '4'))
print(questionset)
输出

Q('2+2', '4') Q('2+2', '4') Q('2*2', '4')
True False
set([Q('2+2', '4'), Q('2*2', '4')])
set([Q('2+3', '5'), Q('2+2', '4'), Q('1+3', '4'), Q('2*3', '6'), Q('2*2', '4')])

请注意,无需测试
问题
实例是否已经是
问题集
的成员。集合元素是唯一的,因此不可能多次添加同一元素。

这就是我作为python noob和凌晨3点编码的结果。非常感谢。