2010-08-11 32 views
34

Tôi quan tâm đến việc sử dụng một dict tùy ý và sao chép nó vào một dict mới, biến đổi nó trên đường đi.Yêu cầu "có thể băm" về giá trị Python

Một đột biến mà tôi muốn làm là trao đổi khóa và giá trị. Thật không may, một số giá trị là dicts ở bên phải của riêng họ. Tuy nhiên, điều này tạo ra một lỗi "unhashable type: 'dict'". Tôi không thực sự tâm trí chỉ việc xâu chuỗi giá trị và cho nó chìa khóa. Nhưng, tôi muốn để có thể làm điều gì đó như thế này:

for key in olddict: 
    if hashable(olddict[key]): 
    newdict[olddict[key]] = key 
    else 
    newdict[str(olddict[key])] = key 

Có cách nào sạch để làm điều này mà không liên quan đến bẫy một ngoại lệ và phân tích các chuỗi thông điệp cho "loại unhashable" ?

Trả lời

38

Kể từ Python 2.6, bạn có thể sử dụng các lớp cơ sở trừu tượng collections.Hashable: ngắn gọn

import collections 
>>> isinstance({}, collections.Hashable) 
False 
>> isinstance(0, collections.Hashable) 
True 

Cách tiếp cận này cũng được đề cập trong tài liệu hướng dẫn cho __hash__.

Doing so means that not only will instances of the class raise an appropriate TypeError when a program attempts to retrieve their hash value, but they will also be correctly identified as unhashable when checking isinstance(obj, collections.Hashable) (unlike classes which define their own __hash__() to explicitly raise TypeError).

+6

Điều này có hoạt động nếu một bộ chứa danh sách/từ điển không? –

+4

Không, và đây là thứ bạn chỉ có thể xác định tại thời gian chạy kể từ trước khi nội dung của danh sách nói chung là không xác định. 'hash (([],))' cho 'TypeError: kiểu không thể loại bỏ: 'list'' – Syncopated

+1

Cảnh báo nhỏ cho những người sử dụng Python 2.7' isinstance (bytearray ([0xa]), collections.Hashable)) 'trả về' True' nhưng 'hash (bytearray ([0xa]))' không thành công với 'TypeError: loại không thể loại bỏ: 'bytearray''. – RedX

13
def hashable(v): 
    """Determine whether `v` can be hashed.""" 
    try: 
     hash(v) 
    except TypeError: 
     return False 
    return True 
+3

Ned - đó là chính xác những gì tôi muốn tránh. Ngoài ra, chức năng này sẽ bẫy TypeErrors không phải là "loại không thể loại bỏ". –

+5

Chỉ có một TypeError mà hash() sẽ tăng lên. Hoặc thay vào đó, bất kỳ hash TypeError nào tăng lên, nó sẽ giữ cho giá trị của bạn không bị băm. Tôi thậm chí còn cho rằng điều này nên bắt ngoại lệ, bởi vì nó không thực sự quan trọng tại sao hash() thất bại: một lỗi trong hash() làm cho giá trị của bạn không thể chạy được. Bạn có thể nói thêm về lý do tại sao bạn muốn tránh ngoại lệ như thế này? Điều này gói gọn xử lý ngoại lệ trong một hàm tốt đẹp và làm cho mã mẫu của bạn ở trên hoạt động hoàn hảo. –

+1

Có lẽ đó là một điểm triết học - câu hỏi không phải là "điều kiện ngoại lệ" của việc không thể chịu được do một số "điều kiện quái dị", mà là một truy vấn về chức năng có sẵn trên một loại. Nếu mà làm cho bất kỳ ý nghĩa. –

1

Tất cả các đối tượng có thể băm được xây dựng bằng python có phương thức .__hash__(). Bạn có thể kiểm tra điều đó.

olddict = {"a":1, "b":{"test":"dict"}, "c":"string", "d":["list"] } 

for key in olddict: 
    if(olddict[key].__hash__): 
     print str(olddict[key]) + " is hashable" 
    else: 
     print str(olddict[key]) + " is NOT hashable" 

đầu ra

1 is hashable 
string is hashable 
{'test': 'dict'} is NOT hashable 
['list'] is NOT hashable 
+1

Cảnh báo về điều này: Trong Python 2.5, điều này sẽ cho: '{ 'test': 'dict'} là hashable'.Nó cũng có thể đưa ra một kết quả sai trong các phiên bản mới hơn nếu một lớp định nghĩa '__hash__' để nâng một TypeError. –

0

Tại sao không sử dụng gõ vịt?

for key in olddict: 
    try: 
     newdict[olddict[key]] = key 
    except TypeError: 
     newdict[str(olddict[key])] = key 
Các vấn đề liên quan