2011-02-09 73 views
9

Tôi đang cố sử dụng một đối tượng làm khóa trong từ điển python, nhưng nó hoạt động theo cách mà tôi không thể hiểu được.đối tượng là các khóa trong từ điển python

Trước tiên tôi tạo ra một từ điển với đối tượng của tôi như là chìa khóa:

package_disseminators = { 
    ContentType("application", "zip", "http://other/property") : "one", 
    ContentType("application", "zip") : "two" 
} 

Bây giờ tạo một đối tượng đó là "giống" như một trong đó là chìa khóa.

content_type = ContentType("application", "zip", "http://other/property") 

tôi đã đưa các đối tượng ContentType tùy chỉnh __eq__ và tùy chỉnh __str__ phương pháp, như vậy mà các phương pháp so sánh __eq____str__ giá trị.

Bây giờ, một số python tương tác:

>>> for key in package_disseminators: 
...  if key == content_type: 
...    print "match" 
...  else: 
...    print "no match" 
... 
no match 
match 

>>> content_type in package_disseminators.keys() 
True 

Ok, vì vậy nó trông giống như đối tượng của tôi là chắc chắn được xác định đúng như một chìa khóa, vì vậy:

>>> package_disseminators[content_type] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: (& (type="application/zip") (packaging="http://other/property")) 

Er ... ok? Vì vậy content_type nằm trong danh sách package_disseminators.keys(), nhưng không phải là khóa?

>>> package_disseminators.has_key(content_type) 
False 

Dường như không.

Tôi đoán rằng quá trình so sánh mà Python sử dụng để xác định bình đẳng khác nhau giữa câu lệnh "trong" thẳng trên danh sách và thực sự tìm kiếm khóa trong dict, nhưng tôi không biết làm thế nào. Bất kỳ mẹo hoặc thông tin chi tiết nào?

Trả lời

17

Từ các tài liệu python:

phím A của từ điển là gần như giá trị tùy ý. Giá trị không phải là giá trị băm, nghĩa là các giá trị có chứa danh sách, từ điển hoặc các loại khác có thể thay đổi (được so sánh theo giá trị thay vì nhận dạng đối tượng) không được sử dụng làm khóa.

Hashable được định nghĩa như sau

Một đối tượng là hashable nếu nó có một băm giá trị mà không bao giờ thay đổi trong đời của nó (nó cần một phương pháp __hash__() ), và có thể được so sánh với khác đối tượng (cần một phương thức __eq__() hoặc __cmp__()). Các đối tượng có thể băm được so sánh bằng nhau phải có cùng giá trị băm .

Tính có thể làm cho một đối tượng có thể sử dụng là khóa từ điển và thành viên được đặt, vì các cấu trúc dữ liệu này sử dụng giá trị băm nội bộ.

Vì vậy, nếu bạn muốn thực hiện việc này, bạn cần ghi đè phương thức mặc định __hash__() trên đối tượng của bạn (xem nhận xét từ Steven Rumbalski bên dưới để được giải thích thêm).


>>> content_type in package_disseminators.keys() 
True 

Tôi cho rằng việc này vì dict.keys() trả về một danh sách, và __contains__ lẽ kiểm tra bình đẳng, nhưng không phải cho băm cùng.

+6

Một số làm rõ thêm: Đối tượng của bạn đã có phương thức '__hash__' được thừa hưởng từ' đối tượng'. Nhưng việc thực hiện mặc định trả về một giá trị duy nhất cho mỗi cá thể, vì vậy hai cá thể bằng nhau sẽ có các hash khác nhau trừ khi bạn cung cấp một triển khai tốt hơn. 'has_key' so sánh giá trị băm,' in' kiểm tra tính bình đẳng, đó là lý do tại sao 'has_key' thất bại trong khi' in' thành công trong ví dụ của bạn. –

+0

Xin chào các bạn. Tuyệt vời, cảm ơn vì điều này, được nhiều người đánh giá cao! –

11

Vì dicts là bảng băm dưới mui xe, bạn cần phải xác định cả hai __eq____hash__ để điều đó hoạt động.

Nguyên tắc cơ bản của ngón tay cái là:

  • Đối với đối tượng mà __eq__ so sánh bình đẳng, __hash__ phải trả lại cùng bảng băm.

Từ mô tả của bạn, một cái gì đó giống như

def __hash__(self): 
    return hash(str(self)) 

nên làm việc.

+0

Cảm ơn bạn đã thực hiện '__hash__'! Thật không may tôi không thể phân bổ hai câu trả lời đúng cho một câu hỏi, mặc dù sự kết hợp của bạn và khác là tất cả mọi thứ mà tôi cần. Chúc mừng, R. –

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