2010-06-19 52 views
19

Tôi mới đến Python, và tôi muốn chắc chắn rằng tôi gạt __eq____hash__ một cách chính xác, để không gây ra lỗi đau đớn sau: (. Tôi đang sử dụng Google App Engine)Python: Đây có phải là cách tốt để ghi đè __eq__ và __hash__ không?

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

Một loại hơi phức tạp hơn:

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

Mọi thứ nhìn tốt?

cập nhật để phản ánh @ ý kiến ​​của 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))) 

Trả lời

14

Người đầu tiên là tốt. Điều thứ hai là vấn đề đối với hai lý do:

  1. có thể có bản sao ở .courses
  2. hai thực thể với giống .courses nhưng khác nhau .forwardLinks sẽ so sánh tương đương nhưng có băm khác nhau

tôi sẽ sửa chữa thứ hai một bằng cách làm cho sự bình đẳng phụ thuộc vào cả hai khóa học và các liên kết chuyển tiếp, nhưng cả hai thay đổi thành bộ (do đó không có bản sao), và cùng cho băm. Tức là .:

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

Điều này tất nhiên là giả định rằng phía trước liên kết quan trọng đối với "giá trị thực" của một đối tượng, nếu không họ nên được bỏ qua từ cả hai __eq____hash__.

Sửa: bị xóa khỏi __hash__ cuộc gọi đến tuple mà là tại dư thừa tốt nhất (và có thể gây tổn hại, theo đề nghị của một bình luận bởi @ Mark [[tx !!!]]); đã thay đổi set thành frozenset trong băm, như được gợi ý bởi nhận xét của @Phillips [[tx !!!]].

+0

Có vẻ ổn. Cảm ơn. –

+0

@Rosarch, bạn được chào đón! –

+1

@ Alex: không phải hàm băm đó phụ thuộc vào thứ tự của các phần tử trong 'tuple (set (self.courses))', có thể có phần tùy ý không? –

Các vấn đề liên quan