2010-06-19 78 views
19

我是新來的Python,我想確保我推翻__eq____hash__正確,以免以後造成痛苦的錯誤:(我使用谷歌應用程序引擎)Python:這是覆蓋__eq__和__hash__的好方法嗎?

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))) 

一切好看嗎?

更新,以反映@亞歷克斯的意見

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))) 

回答

14

第一個是罰款。第二個是有問題的,原因有二:具有相同.courses但不同.forwardLinks.courses

  • 兩個實體

    1. 有可能是重複的會比較平等的,但有不同的散列

    我會解決的第二一個通過平等取決於課程和前向鏈接,但是這兩個集合的變化(因此不重複),並且對於哈希也是如此。即:

    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這充其量冗餘(並可能損壞,通過@馬克[[TX !!!]]評論所建議的)中除去;正如@Phillips [[tx !!!]]的評論所建議的那樣,在散列中將set更改爲frozenset

  • +0

    看起來不錯。謝謝。 – 2010-06-19 19:58:11

    +0

    @Rosarch,不客氣! – 2010-06-19 20:00:00

    +1

    @Alex:那不是散列依賴於'tuple(set(self.courses))'中的元素的順序,這可能有點任意? – 2010-06-19 21:00:22