2012-02-13 37 views
8

Hãy xem xét đoạn mã này:Python 2: ý nghĩa khác nhau của 'trong' từ khóa cho bộ và danh sách

class SomeClass(object): 

    def __init__(self, someattribute="somevalue"): 
     self.someattribute = someattribute 

    def __eq__(self, other): 
     return self.someattribute == other.someattribute 

    def __ne__(self, other): 
     return not self.__eq__(other) 

list_of_objects = [SomeClass()] 
print(SomeClass() in list_of_objects) 

set_of_objects = set([SomeClass()]) 
print(SomeClass() in set_of_objects) 

mà đánh giá để:

True 
False 

bất cứ ai có thể giải thích lý do tại sao 'trong' từ khóa có một ý nghĩa khác cho các bộ và danh sách? Tôi đã mong đợi cả hai để trả về True, đặc biệt là khi loại đang được thử nghiệm có phương pháp bình đẳng được xác định.

+1

Xem http://stackoverflow.com/questions/7549709/unexpected-behavior-for-python-set-contains .. tình cờ, trong Python 3 chạy mã này gợi ý những gì đang xảy ra: "TypeError: unhashable type: ' SomeClass '" – DSM

+0

Cảm ơn tất cả mọi người, tất cả đã rõ ràng. – mskel

+0

BTW, bạn biết rằng 'someattribute' của bạn ở đây là thuộc tính ** class ** chứ không phải thuộc tính ** instance **, đúng không? Bạn * có * nghe nói về '__init__', phải không? –

Trả lời

15

Ý nghĩa là như nhau, nhưng việc triển khai lại khác nhau. Danh sách chỉ đơn giản là kiểm tra từng đối tượng, kiểm tra sự bình đẳng, vì vậy nó hoạt động cho lớp của bạn. Đặt đầu tiên băm các đối tượng và nếu chúng không triển khai hàm băm đúng cách, tập hợp sẽ không hoạt động.

Lớp học của bạn xác định __eq__, nhưng không xác định __hash__ và do đó sẽ không hoạt động bình thường cho bộ hoặc làm khóa từ điển. Quy tắc cho __eq____hash__ là hai đối tượng __eq__ là True cũng phải có băm bằng nhau. Theo mặc định, đối tượng băm dựa trên địa chỉ bộ nhớ của chúng. Vì vậy, hai đối tượng của bạn bằng với định nghĩa của bạn không cung cấp cùng một băm, do đó, chúng phá vỡ quy tắc về __eq____hash__.

Nếu bạn cung cấp triển khai __hash__, nó sẽ hoạt động tốt. Đối với mã mẫu của bạn, nó có thể là:

def __hash__(self): 
    return hash(self.someattribute) 
+4

Đây là một trong những điều mà Python 3 xử lý rõ ràng hơn: nó sẽ từ chối thực hiện một tập hợp bất kỳ đối tượng nào không có '__hash __()'. Python 2 có một '__hash __()' mặc định phản ánh nhận dạng đối tượng chứ không phải là bình đẳng. – lvc

+0

Trên thực tế, những gì xảy ra là các lớp cổ điển hoạt động theo cùng một cách (không xác định phương thức '__hash__' sẽ cho bạn một giá trị mặc định đã tạo ra TypeError nếu bạn đã định nghĩa' __cmp__' và/hoặc '__eq__',) nhưng sau đó là kiểu mới các lớp đã được giới thiệu (trong Python 2.2) và hành vi đó không được sao chép chính xác. Sự giám sát đó đã bị bỏ lỡ cho các bản phát hành đủ để thay đổi nó có khả năng sẽ phá vỡ quá nhiều mã, do đó việc sửa chữa nó đã bị trì hoãn cho đến khi Python 3. –

1

Xác định __hash__() phương pháp tương ứng với phương pháp __eq__() của bạn. Example.

3

Trong hầu hết mọi triển khai có thể bắt đầu, kể cả Python, nếu bạn ghi đè phương pháp bình đẳng, bạn phải ghi đè phương pháp băm (bằng Python, đây là __hash__). Toán tử in cho các danh sách chỉ kiểm tra sự bình đẳng với mọi phần tử trong danh sách, mà toán tử in cho các bộ đầu tiên băm đối tượng bạn đang tìm kiếm, kiểm tra đối tượng trong vùng đó của hashtable, và sau đó kiểm tra xem có bình đẳng không trong khe. Vì vậy, nếu bạn ghi đè __eq__ mà không ghi đè __hash__, bạn không thể được đảm bảo rằng toán tử in cho các bộ sẽ kiểm tra đúng vị trí.

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