2010-06-19 11 views
19

Jestem nowy w Pythonie, i chciałem się upewnić, że overrode __eq____hash__ i prawidłowo, tak aby nie powodować bolesne błędy później: (. Używam Google App Engine)Python: Czy jest to dobry sposób na przesłonięcie __eq__ i __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()) 

nieco bardziej skomplikowany typ:

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

Wszystko wygląda dobrze?

aktualizowane w celu odzwierciedlenia @ komentarze Alexa

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

Odpowiedz

14

Pierwszym z nich jest w porządku. Drugim jest problematyczne z dwóch powodów:

  1. może istnieć duplikaty w .courses
  2. dwóch podmiotów z identycznym .courses ale różnych .forwardLinks byłoby porównanie równe, ale mają różne mieszań

chciałbym naprawić sekundę jeden polegający na tym, że równość zależy zarówno od kursów, jak i od linków do przodu, ale obie zmiany w zestawach (stąd nie ma duplikatów), a tak samo w przypadku mieszania. Tj .:

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

to oczywiście przy założeniu, że do przodu łączy zasadnicze znaczenie dla „prawdziwej” wartości obiektu, w przeciwnym razie należy je pominąć zarówno __eq__ i __hash__.

Edit: usunięty z __hash__ wywołań tuple które były w najlepszym zbędny (i ewentualnie szkodliwe, jak sugeruje komentarz przez @Mark [[TX !!!]]); zmieniono set na frozenset w haszowaniu, zgodnie z sugestią komentarza @Phillips [[tx !!!]].

+0

Wygląda dobrze. Dzięki. –

+0

@Rosarch, nie ma za co! –

+1

@Alex: czy ten skrót nie zależy od kolejności elementów w 'tuple (set (self.courses))', które mogą być nieco dowolne? –

Powiązane problemy