संपादित करें : यदि आपकी सभी कुंजी तार हैं , तो इस उत्तर को पढ़ना जारी रखने से पहले, कृपया जैक ओ'कॉनर का महत्वपूर्ण सरल (और तेज) समाधान देखें (जो हैशिंग नेस्टेड शब्दकोशों के लिए भी काम करता है)।
यद्यपि एक उत्तर को स्वीकार कर लिया गया है, प्रश्न का शीर्षक "हैशिंग ए डिक्शनरी डिक्शनरी" है, और यह उत्तर उस शीर्षक के संबंध में अधूरा है। (जैसा कि प्रश्न के शरीर के संबंध में है, उत्तर पूर्ण है।)
नेकड डिक्शनरी
यदि कोई शब्दकोश को हैश करने के लिए स्टैक ओवरफ्लो की खोज करता है, तो कोई इस शीर्षक वाले प्रश्न पर ठोकर खा सकता है, और यदि कोई हैश के लिए नेस्टेड शब्दकोशों का प्रयास कर रहा है तो असंतुष्ट छोड़ दें। उपरोक्त उत्तर इस मामले में काम नहीं करेगा, और आपको हैश को पुनः प्राप्त करने के लिए किसी प्रकार के पुनरावर्ती तंत्र को लागू करना होगा।
यहाँ एक ऐसा तंत्र है:
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
हैश भी ऐसा ही है, यहां तक कि मैंने फू फू करने के बाद भी। ऐसा इसलिए है क्योंकि फू की पहचान नहीं बदली है, इसलिए हैश वही है। यदि आप अपनी वर्तमान परिभाषा के आधार पर अलग से हैश का फू चाहते हैं, तो समाधान वास्तव में बदल रहा है जो कुछ भी हैश को बंद करना है। इस मामले में, __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()))))
आप इसका उपयोग कर सकते हैं हालांकि आप चाहते हैं कि कई तत्वों की हैश टपल वापस करने के लिए:
# -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__]))
नोट: उपरोक्त सभी कोड पायथन 3.x को मानता है। पहले के संस्करणों में परीक्षण नहीं किया था, हालांकि मुझे लगता है make_hash()
कि 2.7.2 में काम करेंगे। जहां तक उदाहरणों को काम करने की बात है, तो मुझे यह पता है
func.__code__
के साथ प्रतिस्थापित किया जाना चाहिए
func.func_code