แก้ไข : หากคีย์ทั้งหมดของคุณเป็นสตริงดังนั้นก่อนที่จะอ่านคำตอบนี้ต่อไปโปรดดูโซลูชันของ Jack O'Connor ที่เรียบง่ายกว่า (และเร็วกว่า) (ซึ่งใช้ได้กับพจนานุกรมที่ซ้อนกัน)
แม้ว่าคำตอบจะได้รับการยอมรับ แต่ชื่อของคำถามคือ "Hashing a python Dictionary" และคำตอบนั้นไม่สมบูรณ์ตามชื่อเรื่องนั้น (สำหรับเนื้อหาของคำถามคำตอบนั้นสมบูรณ์)
พจนานุกรมที่ซ้อนกัน
หากมีใครค้นหา Stack Overflow สำหรับวิธีการแฮชพจนานุกรมหนึ่งอาจสะดุดกับคำถามที่เหมาะเจาะนี้และปล่อยให้ไม่พอใจถ้ามีใครพยายามแฮชพจนานุกรมที่ซ้อนกันหลายตัว คำตอบข้างต้นจะไม่ทำงานในกรณีนี้และคุณจะต้องใช้กลไกแบบเรียกซ้ำเพื่อดึงข้อมูลแฮช
นี่คือกลไกหนึ่งอย่าง:
import copy
def make_hash(o):
"""
Makes a hash from a dictionary, list, tuple or set to any level, that contains
only other hashable types (including any lists, tuples, sets, and
dictionaries).
"""
if isinstance(o, (set, tuple, list)):
return tuple([make_hash(e) for e in o])
elif not isinstance(o, dict):
return hash(o)
new_o = copy.deepcopy(o)
for k, v in new_o.items():
new_o[k] = make_hash(v)
return hash(tuple(frozenset(sorted(new_o.items()))))
โบนัส: วัตถุและคลาสที่คร่ำครึ
hash()
ฟังก์ชั่นการทำงานที่ดีเมื่อคุณสับเรียนหรืออินสแตนซ์ อย่างไรก็ตามนี่คือปัญหาหนึ่งที่ฉันพบโดยใช้แฮชเกี่ยวกับวัตถุ:
class Foo(object): pass
foo = Foo()
print (hash(foo)) # 1209812346789
foo.a = 1
print (hash(foo)) # 1209812346789
แฮชเหมือนกันแม้หลังจากที่ฉันเปลี่ยนฟู นี่เป็นเพราะตัวตนของ foo ไม่ได้เปลี่ยนดังนั้นแฮชจึงเหมือนกัน หากคุณต้องการ foo hash แตกต่างกันไปขึ้นอยู่กับคำจำกัดความปัจจุบันโซลูชันจะ hash off สิ่งที่เปลี่ยนแปลงจริง ในกรณีนี้__dict__
แอตทริบิวต์:
class Foo(object): pass
foo = Foo()
print (make_hash(foo.__dict__)) # 1209812346789
foo.a = 1
print (make_hash(foo.__dict__)) # -78956430974785
อนิจจาเมื่อคุณพยายามทำสิ่งเดียวกันกับชั้นเรียน:
print (make_hash(Foo.__dict__)) # TypeError: unhashable type: 'dict_proxy'
__dict__
คุณสมบัติคลาสไม่ใช่พจนานุกรมปกติ:
print (type(Foo.__dict__)) # type <'dict_proxy'>
นี่คือกลไกที่คล้ายกันก่อนหน้านี้ที่จะจัดการเรียนอย่างเหมาะสม:
import copy
DictProxyType = type(object.__dict__)
def make_hash(o):
"""
Makes a hash from a dictionary, list, tuple or set to any level, that
contains only other hashable types (including any lists, tuples, sets, and
dictionaries). In the case where other kinds of objects (like classes) need
to be hashed, pass in a collection of object attributes that are pertinent.
For example, a class can be hashed in this fashion:
make_hash([cls.__dict__, cls.__name__])
A function can be hashed like so:
make_hash([fn.__dict__, fn.__code__])
"""
if type(o) == DictProxyType:
o2 = {}
for k, v in o.items():
if not k.startswith("__"):
o2[k] = v
o = o2
if isinstance(o, (set, tuple, list)):
return tuple([make_hash(e) for e in o])
elif not isinstance(o, dict):
return hash(o)
new_o = copy.deepcopy(o)
for k, v in new_o.items():
new_o[k] = make_hash(v)
return hash(tuple(frozenset(sorted(new_o.items()))))
คุณสามารถใช้สิ่งนี้เพื่อส่งกลับ hup tuple ขององค์ประกอบหลายอย่างที่คุณต้องการ:
# -7666086133114527897
print (make_hash(func.__code__))
# (-7666086133114527897, 3527539)
print (make_hash([func.__code__, func.__dict__]))
# (-7666086133114527897, 3527539, -509551383349783210)
print (make_hash([func.__code__, func.__dict__, func.__name__]))
หมายเหตุ: รหัสข้างต้นทั้งหมดถือว่า Python 3.x ไม่ได้ทดสอบในเวอร์ชันก่อนหน้าแม้ว่าฉันmake_hash()
จะถือว่าใช้งานได้แล้วพูดว่า 2.7.2 เท่าที่ทำให้การทำงานตัวอย่างผมไม่ทราบว่า
func.__code__
ควรถูกแทนที่ด้วย
func.func_code