जवाबों:
यहां पायथन डाइक के बारे में सब कुछ है जिसे मैं एक साथ रखने में सक्षम था (शायद किसी को भी अधिक जानना चाहूंगा, लेकिन जवाब व्यापक है)।
dict
का उपयोग करता है (देखें हुक्मबॉक्ट । देखें : 296-297 )।O(1)
सूचकांक द्वारा एक लुकअप कर सकें )।नीचे दिया गया आंकड़ा पायथन हैश तालिका का एक तार्किक प्रतिनिधित्व है। नीचे दी गई आकृति में, 0, 1, ..., i, ...
बाईं ओर हैश तालिका में स्लॉट्स के सूचक हैं (वे सिर्फ उदाहरण के लिए हैं और स्पष्ट रूप से तालिका के साथ संग्रहीत नहीं हैं!)।
# Logical model of Python Hash table
-+-----------------+
0| <hash|key|value>|
-+-----------------+
1| ... |
-+-----------------+
.| ... |
-+-----------------+
i| ... |
-+-----------------+
.| ... |
-+-----------------+
n| ... |
-+-----------------+
जब एक नया तानाशाही शुरू की जाती है तो यह 8 स्लॉट्स से शुरू होती है । (देखें dictobject.h: 49 )
i
, जो कि कुंजी के हैश पर आधारित होता है। सीपीथॉन शुरू में उपयोग करता है i = hash(key) & mask
(जहां mask = PyDictMINSIZE - 1
, लेकिन यह वास्तव में महत्वपूर्ण नहीं है)। बस ध्यान दें कि प्रारंभिक स्लॉट, i
कि जाँच की है कुंजी के हैश पर निर्भर करता है ।<hash|key|value>
)। लेकिन क्या होगा अगर उस स्लॉट पर कब्जा है! सबसे अधिक संभावना है क्योंकि एक और प्रविष्टि में एक ही हैश (हैश टक्कर!) है==
तुलना से नहीं is
) हैश के खिलाफ स्लॉट में प्रविष्टि और वर्तमान प्रविष्टि की कुंजी डालने के लिए है ( तानाशाही विषय) : क्रमशः 337,344-345 )। यदि दोनों मेल खाते हैं, तो यह सोचता है कि प्रविष्टि पहले से मौजूद है, देता है और अगली प्रविष्टि में सम्मिलित होने के लिए आगे बढ़ता है। यदि या तो हैश या कुंजी मेल नहीं खाती है, तो यह जांच शुरू होती है ।i+1, i+2, ...
और पहले उपलब्ध एक का उपयोग कर सकते हैं (यह रैखिक जांच है)। लेकिन टिप्पणियों में खूबसूरती से समझाया गया कारणों के लिए ( डिक्टोबोबेक्ट देखें : 33-126 ), सीपीथॉन यादृच्छिक जांच का उपयोग करता है । यादृच्छिक जांच में, अगले स्लॉट को छद्म यादृच्छिक क्रम में चुना जाता है। प्रविष्टि को पहले खाली स्लॉट में जोड़ा जाता है। इस चर्चा के लिए, अगले स्लॉट को चुनने के लिए उपयोग किया जाने वाला वास्तविक एल्गोरिदम वास्तव में महत्वपूर्ण नहीं है (देखें प्रोब के लिए एल्गोरिथ्म के लिए तानाशाह): 33-126 )। महत्वपूर्ण यह है कि स्लॉट्स तब तक जांचे जाते हैं जब तक कि पहला खाली स्लॉट नहीं मिल जाता।dict
यदि दो-तिहाई भरा हुआ है, तो इसका आकार बदल दिया जाएगा। यह लुक्स को धीमा करने से बचाता है। (देखें तानाशाह। 64: 65 )नोट: मैंने पायथन डिक्ट कार्यान्वयन पर अपने स्वयं के सवाल के जवाब में शोध किया कि कैसे एक तानाशाह में कई प्रविष्टियां समान हैश मान हो सकती हैं। मैंने यहां प्रतिक्रिया का थोड़ा संपादित संस्करण पोस्ट किया है क्योंकि सभी शोध इस प्रश्न के लिए बहुत प्रासंगिक हैं।
पायथन के अंतर्निहित शब्दों को कैसे लागू किया गया है?
यहाँ लघु पाठ्यक्रम है:
आदेश दिया गया पहलू पाइथन 3.6 के रूप में अनौपचारिक है (अन्य कार्यान्वयनों को बनाए रखने का मौका देने के लिए), लेकिन पायनियर 3.7 में आधिकारिक ।
लंबे समय तक इसने ठीक इसी तरह काम किया। अजगर 8 खाली पंक्तियों का प्रचार करेगा और कुंजी-मूल्य जोड़ी को छड़ी करने के लिए निर्धारित करने के लिए हैश का उपयोग करेगा। उदाहरण के लिए, यदि कुंजी के लिए हैश 001 में समाप्त हो गया है, तो इसे 1 (यानी 2 वें) सूचकांक (उदाहरण के लिए नीचे दिया गया है) में चिपका दिया जाएगा।
<hash> <key> <value>
null null null
...010001 ffeb678c 633241c4 # addresses of the keys and values
null null null
... ... ...
प्रत्येक पंक्ति 64 बिट आर्किटेक्चर पर 24 बाइट्स, 32 बिट पर 12 लेती है। (ध्यान दें कि कॉलम हेडर हमारे उद्देश्यों के लिए यहां केवल लेबल हैं - वे वास्तव में मेमोरी में मौजूद नहीं हैं।)
यदि हैश एक preexisting कुंजी के हैश के समान है, तो यह एक टक्कर है, और फिर यह एक अलग स्थान पर कुंजी-मूल्य जोड़ी को चिपकाएगा।
5 कुंजी-मान संग्रहीत होने के बाद, जब एक और कुंजी-मूल्य जोड़ी जोड़ते हैं, तो हैश टकराव की संभावना बहुत बड़ी है, इसलिए शब्दकोश आकार में दोगुना हो जाता है। 64 बिट प्रक्रिया में, आकार बदलने से पहले, हमारे पास 72 बाइट्स खाली हैं, और उसके बाद, हम 10 खाली पंक्तियों के कारण 240 बाइट बर्बाद कर रहे हैं।
इसमें काफी जगह है, लेकिन लुकअप समय काफी स्थिर है। कुंजी तुलना एल्गोरिथ्म हैश की गणना करना है, अपेक्षित स्थान पर जाएं, कुंजी की आईडी की तुलना करें - यदि वे समान वस्तु हैं, तो वे समान हैं। यदि नहीं तो हैश मानों की तुलना करें, यदि वे समान नहीं हैं, तो वे समान नहीं हैं। और फिर, हम अंत में समानता के लिए कुंजी की तुलना करते हैं, और यदि वे समान हैं, तो मान लौटाएं। समानता के लिए अंतिम तुलना काफी धीमी हो सकती है, लेकिन पहले के चेक आमतौर पर अंतिम तुलना को शॉर्टकट करते हैं, जिससे लुकअप बहुत जल्दी हो जाता है।
टकराव की चीजें धीमी हो जाती हैं, और एक हमलावर सैद्धांतिक रूप से हैश टकरावों का उपयोग सेवा हमले से इनकार करने के लिए कर सकता है, इसलिए हमने हैश फ़ंक्शन के आरंभीकरण को ऐसे यादृच्छिक कर दिया कि यह प्रत्येक नई पायथन प्रक्रिया के लिए अलग-अलग हैश की गणना करता है।
ऊपर वर्णित व्यर्थ स्थान ने हमें शब्दकोशों के कार्यान्वयन को संशोधित करने के लिए प्रेरित किया है, एक रोमांचक नई विशेषता के साथ जो अब प्रविष्टि द्वारा आदेश दिए गए हैं।
हम प्रविष्टि के सूचकांक के लिए एक सरणी का प्रचार करके, इसके बजाय शुरू करते हैं।
चूँकि हमारी पहली की-वैल्यू जोड़ी दूसरे स्लॉट में जाती है, इसलिए हम इस तरह इंडेक्स करते हैं:
[null, 0, null, null, null, null, null, null]
और हमारी मेज सिर्फ सम्मिलन क्रम से आबाद हो जाती है:
<hash> <key> <value>
...010001 ffeb678c 633241c4
... ... ...
इसलिए जब हम एक कुंजी के लिए एक लुकअप करते हैं, तो हम उस स्थिति की जांच करने के लिए हैश का उपयोग करते हैं जो हम उम्मीद करते हैं (इस मामले में, हम सीधे सरणी के इंडेक्स 1 में जाते हैं), फिर हैश-टेबल में उस इंडेक्स पर जाएं (जैसे इंडेक्स 0 ), जांचें कि चाबियाँ समान हैं (पहले वर्णित समान एल्गोरिथ्म का उपयोग करके), और यदि ऐसा है, तो मान लौटाएं।
हम निरंतर लुकअप समय को बनाए रखते हैं, कुछ मामलों में मामूली गति के नुकसान के साथ और दूसरों में लाभ, अपसाइड के साथ कि हम पूर्व-मौजूदा कार्यान्वयन पर काफी जगह बचाते हैं और हम प्रविष्टि क्रम को बनाए रखते हैं। इंडेक्स ऐरे में केवल रिक्त स्थान व्यर्थ बाइट्स हैं।
रेमंड हेटिंगर ने 2012 के दिसंबर में अजगर-देव पर इसे पेश किया । यह अंत में पायथन 3.6 में सीपीथॉन में मिला । सम्मिलन द्वारा आदेश देना 3.6 के लिए एक कार्यान्वयन विवरण माना जाता था ताकि पायथन के अन्य कार्यान्वयन को पकड़ने का मौका मिल सके।
अंतरिक्ष को बचाने के लिए एक और अनुकूलन एक कार्यान्वयन है जो चाबियाँ साझा करता है। इस प्रकार, निरर्थक शब्दकोशों के बजाय जो उस स्थान को पूरा करते हैं, हमारे पास ऐसे शब्दकोश हैं जो साझा कुंजी और कुंजियों के हैश का पुन: उपयोग करते हैं। आप इसे इस तरह से सोच सकते हैं:
hash key dict_0 dict_1 dict_2...
...010001 ffeb678c 633241c4 fffad420 ...
... ... ... ... ...
64 बिट मशीन के लिए, यह 16 बाइट्स प्रति कुंजी प्रति अतिरिक्त शब्दकोश बचा सकता है।
इन साझा-कुंजी डिकेट का उपयोग कस्टम ऑब्जेक्ट्स के लिए किया जाना है __dict__
। इस व्यवहार को प्राप्त करने के लिए, मेरा मानना है कि आपको __dict__
अपनी अगली वस्तु को पलटने से पहले अपनी आबादी को खत्म करने की आवश्यकता है ( पीईपी 412 देखें )। इसका मतलब है कि आपको अपनी सभी विशेषताओं को __init__
या में असाइन करना चाहिए __new__
, अन्यथा आपको अपनी अंतरिक्ष बचत नहीं मिल सकती है।
हालाँकि, यदि आप अपने __init__
निष्पादन के समय अपनी सभी विशेषताओं को जानते हैं, तो आप __slots__
अपनी वस्तु के लिए भी प्रदान कर सकते हैं , और गारंटी जो कि __dict__
बिल्कुल भी नहीं बनाई गई है (यदि माता-पिता में उपलब्ध नहीं है), या यहां तक कि अनुमति दें __dict__
लेकिन गारंटी दें कि आपकी पूर्वाभास विशेषताएँ हैं किसी भी तरह स्लॉट में संग्रहीत। अधिक जानकारी के लिए __slots__
, मेरा जवाब यहां देखें ।
**kwargs
एक समारोह में आदेश का संरक्षण ।find_empty_slot
: github.com/python/cpython/blob/master/Objects/dictobject.c # L969 - और लाइन 134 पर शुरू होने वाला कुछ गद्य है जो इसका वर्णन करता है।
पायथन डिक्शनर्स ओपन एड्रेसिंग ( सुंदर कोड के अंदर संदर्भ ) का उपयोग करते हैं
नायब! ओपन एड्रेसिंग , उर्फ बंद हैशिंग , जैसा कि विकिपीडिया में उल्लेखित है, इसके विपरीत ओपन हैशिंग के साथ भ्रमित नहीं होना चाहिए !
ओपन एड्रेसिंग का अर्थ है कि ताना सरणी स्लॉट्स का उपयोग करता है, और जब किसी वस्तु की प्राथमिक स्थिति को तानाशाही में लिया जाता है, तो वस्तु का स्थान उसी क्रम में एक अलग अनुक्रमणिका में "परबर्शन" स्कीम का उपयोग करते हुए मांगा जाता है, जहां वस्तु का हैश मान भाग होता है। ।