2010-06-19 10 views
19

Soy nuevo en Python, y quería asegurarse de que hizo caso omiso de __eq____hash__ y correctamente, a fin de no causar errores dolorosos más tarde: (. Estoy usando Google App Engine)Python: ¿Es esta una buena forma de anular __eq__ y __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 poco más complicado:

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

Todo se ve bien?

actualizado para reflejar los comentarios de 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))) 

Respuesta

14

El primero de ellos está muy bien. El segundo es problemático por dos razones:

  1. podría ser duplicados en .courses
  2. dos entidades con idéntica .courses pero diferente .forwardLinks compararía iguales, pero tienen valores hash diferentes

Me fijar el segundo uno al hacer que la igualdad dependa tanto de los cursos como de los enlaces hacia adelante, pero ambos cambian a los conjuntos (por lo tanto, no hay duplicados), y lo mismo para el hash. Es decir .:

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

Por supuesto, esto es suponiendo que une el delantero son cruciales para "valor real" de un objeto, de lo contrario, debe omitirse tanto __eq__ y __hash__.

Editar: retirado de __hash__ llamadas a tuple que eran, en el mejor redundante (y posiblemente perjudicial, según lo sugerido por un comentario de @ Marcos [[tx !!!]]); cambió set a frozenset en hash, como lo sugiere un comentario de @Phillips [[tx !!!]].

+0

Se ve bien. Gracias. –

+0

@Rosarch, de nada! –

+1

@Alex: ¿ese hash no depende del orden de los elementos en 'tuple (set (self.courses))', que podría ser algo arbitrario? –

Cuestiones relacionadas