แปลพจนานุกรม


156

เพื่อวัตถุประสงค์ในการแคชฉันต้องสร้างแคชคีย์จากข้อโต้แย้ง GET ซึ่งมีอยู่ใน dict

ขณะนี้ฉันกำลังใช้sha1(repr(sorted(my_dict.items())))( sha1()เป็นวิธีการอำนวยความสะดวกที่ใช้hashlibภายใน) แต่ฉันอยากรู้ว่ามีวิธีที่ดีกว่านี้หรือไม่


4
สิ่งนี้อาจไม่ทำงานกับ dict ที่ซ้อนกัน ทางออกที่สั้นที่สุดคือการใช้ json.dumps (my_dict, sort_keys = True) แทนซึ่งจะคืนค่าเป็นค่า dict
Andrey Fedorov

2
FYI Re: dumps, stackoverflow.com/a/12739361/1082367กล่าวว่า "ผลลัพธ์จาก pickle ไม่ได้รับประกันว่าจะได้รับการยอมรับด้วยเหตุผลที่คล้ายกันกับ dict และตั้งค่าคำสั่งที่ไม่ได้กำหนดห้ามใช้ pickle หรือ pprint หรือ repr สำหรับ hashing ."
Matthew Cornell

เรียงลำดับแป้น dict ไม่ใช่รายการฉันจะส่งคีย์ไปยังฟังก์ชันแฮช
nyuwec

2
backstory ที่น่าสนใจเกี่ยวกับการแปลงโครงสร้างข้อมูลที่ไม่แน่นอน (เช่นพจนานุกรม): python.org/dev/peps/pep-0351ถูกเสนอให้อนุญาตวัตถุที่แช่แข็งโดยพลการ แต่ถูกปฏิเสธ สำหรับเหตุผลดูหัวข้อนี้ใน python-dev: mail.python.org/pipermail/python-dev/2006-F กุมภาพันธ์/060793.html
FluxLemur

ถ้าข้อมูลของคุณเป็นรูปแบบ JSON และคุณต้องการคร่ำเครียดคงความหมายชำระเงินgithub.com/schollii/sandals/blob/master/json_sem_hash.py มันทำงานกับโครงสร้างที่ซ้อนกัน (แน่นอนตั้งแต่ json) และไม่ขึ้นอยู่กับ internals of dict เช่นคำสั่งที่เก็บรักษาไว้ (ซึ่งมีการพัฒนาตลอดช่วงชีวิตของ python) และจะให้แฮชเดียวกันถ้าโครงสร้างข้อมูลทั้งสองมีความหมายเดียวกัน ( ชอบ{'a': 1, 'b':2}มีความหมายเหมือนกับ{'b':2, 'a':1}) ฉันไม่ได้ใช้มันกับอะไรที่ซับซ้อนเกินไป YMMV แต่ยินดีให้คำติชม
Oliver

คำตอบ:


110

หากพจนานุกรมของคุณไม่ซ้อนคุณสามารถสร้างไอเท็มได้หลายอย่างพร้อมไอเท็มของ dict และใช้hash():

hash(frozenset(my_dict.items()))

นี่คือการคำนวณที่เข้มข้นน้อยกว่าการสร้างสตริง JSON หรือการแสดงพจนานุกรม

อัปเดต: โปรดดูความคิดเห็นด้านล่างสาเหตุที่วิธีการนี้อาจไม่ให้ผลลัพธ์ที่มั่นคง


9
สิ่งนี้ไม่ได้ผลสำหรับฉันด้วยพจนานุกรมที่ซ้อนกัน ฉันไม่ได้ลองวิธีแก้ปัญหาด้านล่าง (ซับซ้อนเกินไป) โซลูชันของ OP ทำงานได้อย่างสมบูรณ์แบบ ฉันแทนที่ sha1 ด้วยแฮชเพื่อบันทึกการนำเข้า
spatel

9
@Ceaser ไม่สามารถใช้งานได้เพราะ tuple แสดงถึงการสั่งซื้อ แต่รายการ dict นั้นไม่ได้เรียงลำดับ frozenset ดีกว่า
พลวง

28
ระวังแฮชในตัวหากมีบางสิ่งที่จำเป็นต้องสอดคล้องกันในเครื่องที่แตกต่างกัน การดําเนินการของงูหลามบนแพลตฟอร์มคลาวด์เช่น Heroku และ GAE จะกลับค่าที่แตกต่างกันสำหรับกัญชา () ในกรณีที่แตกต่างกันทำให้มันไม่มีประโยชน์อะไรที่จะต้องร่วมกันระหว่างสองคนหรือมากกว่า "เครื่องจักร" (dynos ในกรณีของ Heroku) ที่
เบนโรเบิร์ต

6
มันอาจจะน่าสนใจhash()ฟังก์ชั่นไม่ได้สร้างผลลัพธ์ที่มั่นคง นี่หมายความว่าเมื่อได้รับอินพุตเดียวกันมันจะส่งคืนผลลัพธ์ที่แตกต่างกันด้วยอินสแตนซ์ต่าง ๆ ของล่ามหลาม สำหรับฉันดูเหมือนว่าจะมีการสร้างคุณค่าของเมล็ดพันธุ์ทุกครั้งที่มีการแปลล่าม
Hermann Schachner

7
ที่คาดหวัง เมล็ดถูกนำมาใช้เพื่อเหตุผลด้านความปลอดภัยเท่าที่ฉันจำได้ว่าจะเพิ่มการสุ่มหน่วยความจำบางประเภท ดังนั้นคุณจึงไม่สามารถคาดหวังว่าแฮชจะเหมือนกันระหว่างกระบวนการไพ ธ อนสองกระบวนการ
Nikokrock

137

การใช้sorted(d.items())ไม่เพียงพอที่จะให้เราตอบโต้อย่างมีเสถียรภาพ บางค่าในdพจนานุกรมอาจเป็นเกินไปและกุญแจของพวกเขาจะยังคงออกมาในลำดับโดยพลการ ตราบใดที่คีย์ทั้งหมดเป็นสตริงฉันต้องการใช้:

json.dumps(d, sort_keys=True)

ที่กล่าวว่าหากแฮชจำเป็นต้องมีเสถียรภาพในเครื่องที่แตกต่างกันหรือรุ่น Python ฉันไม่แน่ใจว่านี่เป็นกระสุน คุณอาจต้องการเพิ่มseparatorsและensure_asciiอาร์กิวเมนต์เพื่อป้องกันตัวคุณเองจากการเปลี่ยนแปลงใด ๆ ในค่าเริ่มต้นที่นั่น ฉันขอขอบคุณความคิดเห็น


6
นี่เป็นเพียงการหวาดระแวง แต่ JSON อนุญาตให้ตัวละครส่วนใหญ่แสดงเป็นสตริงโดยไม่มีการหลบหนีตามตัวอักษรดังนั้นตัวเข้ารหัสจะเลือกตัวเลือกบางอย่างเกี่ยวกับการหลบหนีตัวละครหรือผ่านพวกเขาไป ความเสี่ยงก็คือเวอร์ชั่นที่แตกต่างกัน (หรือเวอร์ชั่นในอนาคต) ของเครื่องเข้ารหัสอาจเลือกตัวเลือกการหลบหนีที่แตกต่างกันตามค่าเริ่มต้นจากนั้นโปรแกรมของคุณจะคำนวณค่าแฮชที่แตกต่างกันสำหรับพจนานุกรมเดียวกันในสภาพแวดล้อมที่แตกต่างกัน การensure_asciiโต้แย้งจะป้องกันปัญหาสมมุติฐานทั้งหมดนี้
Jack O'Connor

4
ฉันทดสอบประสิทธิภาพของสิ่งนี้ด้วยชุดข้อมูลที่แตกต่างกันมันเร็วกว่าmake_hashมาก gist.github.com/charlax/b8731de51d2ea86c6eb9
charlax

3
@charlax ujson ไม่รับประกันการสั่งซื้อของคู่ dict ดังนั้นจึงไม่ปลอดภัยที่จะทำเช่นนั้น
arthurprs

11
โซลูชันนี้ทำงานได้ตราบใดที่คีย์ทั้งหมดเป็นสตริงเช่น json.dumps ({'a': {(0, 5): 5, 1: 3}}) ล้มเหลว
kadee

5
@LorenzoBelli คุณสามารถเอาชนะได้โดยการเพิ่มdefault=strกับdumpsคำสั่ง ดูเหมือนว่าจะทำงานอย่างดี
mlissner

63

แก้ไข : หากคีย์ทั้งหมดของคุณเป็นสตริงดังนั้นก่อนที่จะอ่านคำตอบนี้ต่อไปโปรดดูโซลูชันของ 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

isinstance รับลำดับสำหรับอาร์กิวเมนต์ที่สองดังนั้น isinstance (o, (set, tuple, list)) จะใช้งานได้
Xealot

ขอบคุณที่ทำให้ผมตระหนัก frozenset สามารถทำได้อย่างต่อเนื่องพารามิเตอร์กัญชาสตริงการสืบค้น :)
Xealot

1
รายการจะต้องมีการเรียงลำดับเพื่อสร้างแฮชเดียวกันถ้าคำสั่งไอเท็ม dict แตกต่างกัน แต่ค่าคีย์ไม่ได้ -> แฮชคืน (tuple (frozenset (เรียง (new_o.items ()))))
Bas Koopmans

ดี! ฉันยังเพิ่มการโทรไปยังhashรายการต่างๆและสิ่งอันดับ มิฉะนั้นจะต้องใช้รายการจำนวนเต็มของฉันที่เกิดขึ้นเป็นค่าในพจนานุกรมของฉันและส่งกลับรายการของแฮชซึ่งไม่ใช่สิ่งที่ฉันต้องการ
osa

frozenset เป็นคอลเล็กชั่น UNORDERED ดังนั้นจึงไม่มีอะไรที่จะได้รับจากการจัดเรียงอินพุต รายการและสิ่งอันดับอื่น ๆ คือชุดสะสม ORDERED ("ลำดับ") ดังนั้นค่าแฮชควรได้รับผลกระทบจากคำสั่งของรายการภายใน คุณไม่ควรจัดเรียง!
RobM

14

นี่คือทางออกที่ชัดเจนกว่า

def freeze(o):
  if isinstance(o,dict):
    return frozenset({ k:freeze(v) for k,v in o.items()}.items())

  if isinstance(o,list):
    return tuple([freeze(v) for v in o])

  return o


def make_hash(o):
    """
    makes a hash out of anything that contains only list,dict and hashable types including string and numeric types
    """
    return hash(freeze(o))  

หากคุณเปลี่ยนif isinstance(o,list):เป็นif isinstance(obj, (set, tuple, list)):ฟังก์ชั่นนี้สามารถทำงานกับวัตถุใด ๆ
Peter Schorn

10

รหัสด้านล่างหลีกเลี่ยงการใช้ฟังก์ชั่น Python hash () เพราะจะไม่ให้แฮชที่สอดคล้องกันตลอดการรีสตาร์ท Python (ดูฟังก์ชันแฮชใน Python 3.3 จะให้ผลลัพธ์ที่แตกต่างกันระหว่างเซสชัน ) make_hashable()จะแปลงวัตถุเป็น tuples ที่ซ้อนกันและmake_hash_sha256()จะแปลงrepr()เป็นแฮช SHA256 ที่เข้ารหัส 64 ด้วย

import hashlib
import base64

def make_hash_sha256(o):
    hasher = hashlib.sha256()
    hasher.update(repr(make_hashable(o)).encode())
    return base64.b64encode(hasher.digest()).decode()

def make_hashable(o):
    if isinstance(o, (tuple, list)):
        return tuple((make_hashable(e) for e in o))

    if isinstance(o, dict):
        return tuple(sorted((k,make_hashable(v)) for k,v in o.items()))

    if isinstance(o, (set, frozenset)):
        return tuple(sorted(make_hashable(e) for e in o))

    return o

o = dict(x=1,b=2,c=[3,4,5],d={6,7})
print(make_hashable(o))
# (('b', 2), ('c', (3, 4, 5)), ('d', (6, 7)), ('x', 1))

print(make_hash_sha256(o))
# fyt/gK6D24H9Ugexw+g3lbqnKZ0JAcgtNW+rXIDeU2Y=

1
make_hash_sha256(((0,1),(2,3)))==make_hash_sha256({0:1,2:3})==make_hash_sha256({2:3,0:1})!=make_hash_sha256(((2,3),(0,1))). นี่ไม่ใช่วิธีการแก้ปัญหาที่ฉันกำลังมองหา แต่มันเป็นสื่อกลางที่ดี ฉันกำลังคิดที่type(o).__name__จะเพิ่มจุดเริ่มต้นของสิ่งอันดับแต่ละอย่างเพื่อบังคับการสร้างความแตกต่าง
Poik

ถ้าคุณต้องการเรียงลำดับรายการเช่นกัน:tuple(sorted((make_hashable(e) for e in o)))
Suraj

make_hash_sha256 () - ดีมาก!
jtlz2

1
@Suraj คุณไม่ควรต้องการเรียงลำดับรายการก่อนที่จะ hashing เนื่องจากรายการที่มีเนื้อหาในคำสั่งซื้อที่แตกต่างกันไม่เหมือนกันแน่นอน หากลำดับของรายการไม่สำคัญว่าปัญหาคือคุณกำลังใช้โครงสร้างข้อมูลที่ไม่ถูกต้อง คุณควรใช้ชุดแทนที่จะเป็นรายการ
scottclowe

@scottclowe นั่นเป็นเรื่องจริงมาก ขอบคุณที่เพิ่มจุดนั้น มี 2 ​​สถานการณ์ที่คุณยังต้องการรายการ (โดยไม่จำเป็นต้องมีคำสั่งซื้อ) - 1. รายการของรายการที่ทำซ้ำ 2. เมื่อคุณใช้ JSON โดยตรง ในฐานะที่เป็น JSON ไม่สนับสนุนการเป็นตัวแทน "ชุด"
Suraj

5

อัปเดตจาก 2013 คำตอบ ...

คำตอบข้างต้นดูเหมือนจะไม่น่าเชื่อถือสำหรับฉัน เหตุผลคือการใช้รายการ () เท่าที่ฉันรู้สิ่งนี้ออกมาในลำดับที่ขึ้นอยู่กับเครื่อง

แล้วเรื่องนี้ล่ะ

import hashlib

def dict_hash(the_dict, *ignore):
    if ignore:  # Sometimes you don't care about some items
        interesting = the_dict.copy()
        for item in ignore:
            if item in interesting:
                interesting.pop(item)
        the_dict = interesting
    result = hashlib.sha1(
        '%s' % sorted(the_dict.items())
    ).hexdigest()
    return result

คุณคิดว่าเหตุใดจึงสำคัญที่dict.itemsไม่ส่งคืนรายการที่มีการสั่งซื้อล่วงหน้า frozensetดูแลสิ่งนั้น
เกลริน

2
ชุดโดยนิยามไม่ได้เรียงลำดับ ดังนั้นลำดับในการเพิ่มวัตถุจึงไม่เกี่ยวข้อง คุณต้องตระหนักว่าฟังก์ชั่นในตัวhashไม่สนใจว่าจะพิมพ์เนื้อหา frozenset หรืออะไรแบบนั้น ทดสอบในหลายเครื่องและเวอร์ชั่นหลามแล้วคุณจะเห็น
glarrain

เหตุใดคุณจึงใช้แฮชพิเศษ () ในการเรียกค่า value = hash ('% s ::% s'% (ค่าประเภท (ค่า)))?
RuiDo

4

เพื่อรักษาคำสั่งซื้อที่สำคัญแทนที่จะเป็นhash(str(dictionary))หรือhash(json.dumps(dictionary))ฉันต้องการโซลูชันที่รวดเร็วและสกปรก:

from pprint import pformat
h = hash(pformat(dictionary))

มันจะทำงานได้แม้กับประเภทที่ต้องการDateTimeและอื่น ๆ ที่ไม่ใช่ JSON


3
ใครรับประกันว่า pformat หรือ json จะใช้คำสั่งเดียวกันเสมอ
ThiefMaster

1
@ThiefMaster "การเปลี่ยนแปลงในรุ่น 2.5:. พจนานุกรมจะเรียงตามคีย์ก่อนการแสดงผลคำนวณ; ก่อน 2.5 พจนานุกรมถูกเรียงเฉพาะในกรณีที่จำเป็นต้องมีการแสดงผลมากกว่าหนึ่งบรรทัดถึงแม้ว่ามันจะไม่ได้รับเอกสาร" ( docs.python org / 2 / library / pprint.html )
Arel

2
ดูเหมือนจะไม่ถูกต้องสำหรับฉัน ผู้เขียนเข้าใจโมดูล pprint และ pformat เพื่อจุดประสงค์ในการแสดงผลไม่ใช่การทำให้เป็นอนุกรม ด้วยเหตุนี้คุณไม่ควรรู้สึกปลอดภัยถ้าสมมติว่า pformat จะส่งคืนผลลัพธ์ที่เกิดขึ้นกับการทำงานเสมอ
David Sanders

3

คุณสามารถใช้frozendictโมดูลของบุคคลที่สามเพื่อตรึง dict ของคุณและทำให้มันแฮช

from frozendict import frozendict
my_dict = frozendict(my_dict)

สำหรับการจัดการวัตถุที่ซ้อนกันคุณสามารถไปกับ:

import collections.abc

def make_hashable(x):
    if isinstance(x, collections.abc.Hashable):
        return x
    elif isinstance(x, collections.abc.Sequence):
        return tuple(make_hashable(xi) for xi in x)
    elif isinstance(x, collections.abc.Set):
        return frozenset(make_hashable(xi) for xi in x)
    elif isinstance(x, collections.abc.Mapping):
        return frozendict({k: make_hashable(v) for k, v in x.items()})
    else:
        raise TypeError("Don't know how to make {} objects hashable".format(type(x).__name__))

หากคุณต้องการรองรับประเภทอื่น ๆ ให้ใช้functools.singledispatch(Python 3.7):

@functools.singledispatch
def make_hashable(x):
    raise TypeError("Don't know how to make {} objects hashable".format(type(x).__name__))

@make_hashable.register
def _(x: collections.abc.Hashable):
    return x

@make_hashable.register
def _(x: collections.abc.Sequence):
    return tuple(make_hashable(xi) for xi in x)

@make_hashable.register
def _(x: collections.abc.Set):
    return frozenset(make_hashable(xi) for xi in x)

@make_hashable.register
def _(x: collections.abc.Mapping):
    return frozendict({k: make_hashable(v) for k, v in x.items()})

# add your own types here

นี้ไม่ได้ทำงานตัวอย่างเช่นหาdictของDataFrameวัตถุ
James Hirschorn

@JamesHirschorn: อัพเดทล้มเหลวเสียงดัง
Eric

ดีกว่า! ฉันได้เพิ่มส่วนelifคำสั่งต่อไปนี้เพื่อให้มันทำงานกับDataFrames: elif isinstance(x, pd.DataFrame): return make_hashable(hash_pandas_object(x).tolist()) ฉันจะแก้ไขคำตอบและดูว่าคุณยอมรับหรือไม่ ...
James Hirschorn

1
ตกลง. ฉันเห็นว่าฉันกำลังขออะไรมากกว่า "hashable" ซึ่งรับประกันได้ว่าวัตถุที่เท่ากันจะมีแฮชเดียวกัน ฉันกำลังทำงานกับรุ่นที่จะให้ค่าเดียวกันระหว่างวิ่งและเป็นอิสระจากรุ่นหลาม ฯลฯ ..
เจมส์ Hirschorn

1
hashการสุ่มเป็นคุณสมบัติความปลอดภัยที่เปิดใช้งานโดยค่าเริ่มต้นใน python 3.7
Eric

1

คุณสามารถใช้ไลบรารีแผนที่เพื่อทำสิ่งนี้ โดยเฉพาะแผนที่ FrozenMap

import maps
fm = maps.FrozenMap(my_dict)
hash(fm)

หากต้องการติดตั้งmapsเพียงทำ:

pip install maps

มันจัดการdictกรณีซ้อนกันเช่นกัน:

import maps
fm = maps.FrozenMap.recurse(my_dict)
hash(fm)

คำเตือน: ฉันเป็นผู้เขียนของmapsห้องสมุด


ไลบรารีไม่เรียงลำดับรายการภายใน dict และด้วยเหตุนี้สิ่งนี้สามารถผลิตแฮชที่แตกต่างกัน ควรมีตัวเลือกในการเรียงลำดับรายการด้วย frozenset น่าจะช่วยได้ แต่ฉันสงสัยว่าคุณจะจัดการกับคดีด้วยพจน์ซ้อนซ้อนที่มีรายชื่อ dicts อย่างไร ในฐานะที่เป็น dict unhashable
Suraj

1
@Suraj: มันไม่.recurseจับโครงสร้างที่ซ้อนกันผ่านทาง ดูmaps.readthedocs.io/en/latest/api.html#maps.FrozenMap.recurse .recurseการสั่งซื้อในรายการมีความหมายความหมายถ้าคุณต้องการความเป็นอิสระเพื่อที่คุณสามารถแปลงรายการของคุณชุดก่อนที่จะเรียก นอกจากนี้คุณยังสามารถใช้list_fnพารามิเตอร์เพื่อ.recurseใช้โครงสร้างข้อมูลแบบtuplefrozenset
.eg


-8

ฉันทำแบบนี้:

hash(str(my_dict))

1
บางคนสามารถอธิบายสิ่งที่ผิดพลาดได้ด้วยวิธีนี้
mhristache

7
@maximi พจนานุกรมไม่มั่นคงตามข้อกำหนดดังนั้นhash(str({'a': 1, 'b': 2})) != hash(str({'b': 2, 'a': 1}))(ในขณะที่อาจใช้ได้กับพจนานุกรมบางรุ่นไม่รับประกันว่าจะทำงานได้ทั้งหมด)
Vlad Frolov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.