【问题标题】:Python: Is this an ok way of overriding __eq__ and __hash__?Python:这是覆盖 __eq__ 和 __hash__ 的好方法吗?
【发布时间】:2011-03-05 20:29:00
【问题描述】:

我是 Python 新手,我想确保我正确覆盖了 __eq____hash__,以免以后造成痛苦的错误:

(我使用的是 Google App Engine。)

class Course(db.Model):
    dept_code = db.StringProperty()
    number = db.IntegerProperty()
    title = db.StringProperty()
    raw_pre_reqs = db.StringProperty(multiline=True)
    original_description = db.StringProperty()

    def getPreReqs(self):
        return pickle.loads(str(self.raw_pre_reqs))

    def __repr__(self):
        title_msg = self.title if self.title else "Untitled"
        return "%s %s: %s" % (self.dept_code, self.number, title_msg)

    def __attrs(self):
        return (self.dept_code, self.number, self.title, self.raw_pre_reqs, self.original_description)

    def __eq__(self, other):
        return isinstance(other, Course) and self.__attrs() == other.__attrs()

    def __hash__(self):
        return hash(self.__attrs())

稍微复杂一点的类型:

class DependencyArcTail(db.Model):
    ''' A list of courses that is a pre-req for something else '''
    courses = db.ListProperty(db.Key)

    ''' a list of heads that reference this one '''
    forwardLinks = db.ListProperty(db.Key)

    def __repr__(self):
        return "DepArcTail %d: courses='%s' forwardLinks='%s'" % (id(self), getReprOfKeys(self.courses), getIdOfKeys(self.forwardLinks))

    def __eq__(self, other):
        if not isinstance(other, DependencyArcTail):
            return False

        for this_course in self.courses:
            if not (this_course in other.courses):
                return False

        for other_course in other.courses:
            if not (other_course in self.courses):
                return False

        return True

    def __hash__(self):
        return hash((tuple(self.courses), tuple(self.forwardLinks)))

一切看起来都不错?

更新以反映 @Alex 的 cmets

class DependencyArcTail(db.Model):
    ''' A list of courses that is a pre-req for something else '''
    courses = db.ListProperty(db.Key)

    ''' a list of heads that reference this one '''
    forwardLinks = db.ListProperty(db.Key)

    def __repr__(self):
        return "DepArcTail %d: courses='%s' forwardLinks='%s'" % (id(self), getReprOfKeys(self.courses), getIdOfKeys(self.forwardLinks))

    def __eq__(self, other):
        return isinstance(other, DependencyArcTail) and set(self.courses) == set(other.courses) and set(self.forwardLinks) == set(other.forwardLinks)

    def __hash__(self):
        return hash((tuple(self.courses), tuple(self.forwardLinks)))

【问题讨论】:

    标签: python hash


    【解决方案1】:

    第一个很好。第二个问题有两个原因:

    1. .courses 中可能有重复项
    2. 具有相同.courses 但不同.forwardLinks 的两个实体比较相等,但具有不同的哈希值

    我将通过使相等性取决于课程和前向链接来解决第二个问题,但是对集合的更改(因此没有重复)和散列相同。即:

    def __eq__(self, other):
        if not isinstance(other, DependencyArcTail):
            return False
    
        return (set(self.courses) == set(other.courses) and
                set(self.forwardLinks) == set(other.forwardLinks))
    
    def __hash__(self):
        return hash((frozenset(self.courses), frozenset(self.forwardLinks)))
    

    这当然是假设前向链接对一个对象的“真实价值”至关重要,否则它们应该从__eq____hash__ 中省略。

    编辑:从__hash__tuple 的调用中删除,这些调用充其量是多余的(并且可能具有破坏性,正如@Mark [[tx!!!]] 的评论所建议的那样);正如@Phillips [[tx!!!]] 的评论所建议的,在散列中将set 更改为frozenset

    【讨论】:

    • @Alex:这个哈希值不取决于tuple(set(self.courses)) 中元素的顺序,这可能有点武断吗?
    • @Mark,它是任意的,但不是反复无常的——尽管我不能 100% 确定具有相同项目的任意打乱列表会产生相同的有序集(它可能因 Python 版本而异) ,所以最好避免我懒惰地留下的tuple 电话——让我相应地编辑,谢谢。
    • 我认为你应该使用frozenset 而不是set,后者是不可散列的。
    • 当您覆盖等于时,您是否建议不等于部分也被覆盖以保持一致的行为stackoverflow.com/questions/390250/…
    • 这是一个不完整的答案。请查看here。几点:1.你应该实现__ne__(并且可能使用total_ordering。2.当你不知道如何相等时,你应该返回NotImplemented
    猜你喜欢
    • 2020-08-28
    • 2011-03-28
    • 2017-12-23
    • 2016-11-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-13
    • 2015-08-01
    • 2017-03-14
    相关资源
    最近更新 更多