2010-06-19 11 views
19

Sono nuovo di Python, e ho voluto fare in modo che ho calpestato __eq__ e __hash__ correttamente, in modo da non causare errori dolorose in seguito: (. Sto usando Google App Engine)Python: È un modo corretto di ignorare __eq__ e __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()) 

Un tipo un po 'più complicato:

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

Tutto guardare bene?

aggiornati per riflettere @ commenti di Alex

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

risposta

14

La prima è bene. Il secondo è problematico per due ragioni:

  1. ci potrebbero essere duplicati in .courses
  2. due entità uguali .courses ma diversi .forwardLinks paragonerei uguali, ma hanno gli hash differenti

vorrei risolvere la seconda uno facendo in modo che l'uguaglianza dipenda da entrambi i corsi e dai collegamenti avanzati, ma entrambi cambiano in insiemi (quindi nessun duplicato) e lo stesso per l'hashing. Cioè .:

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

Questo naturalmente è supponendo che l'attaccante collega sono cruciali per "valore reale" di un oggetto, altrimenti dovrebbero essere omessi sia __eq__ e __hash__.

Edit: rimosso dal __hash__ chiamate a tuple che erano nel migliore dei casi ridondanti (e forse dannoso, come suggerito da un commento di @ Mark [[tx !!!]]); modificato set a frozenset nell'hashing, come suggerito da un commento di @Phillips [[tx !!!]].

+0

Sembra buono. Grazie. –

+0

@Rosarch, prego! –

+1

@Alex: questo hash non dipende dall'ordine degli elementi in 'tuple (set (self.courses))', che potrebbe essere alquanto arbitrario? –

Problemi correlati