2017-06-26 18 views
6

उस पर पुनरावृत्ति करते समय एक शब्दकोश को संशोधित करना। पाइथन में बग dict?

d = {1: 1} 
for k in d.keys(): 
    d['{}'.format(k)] = d.pop(k) 
print(d) 

के उत्पादन {'1': 1} है।

d = {1: 1} 
for k in d.keys(): 
    d['i{}'.format(k)] = d.pop(k) 
print(d) 

के उत्पादन {'iiiii1': 1} है। क्या यह एक बग है? मैं Python 3.6.1 :: Anaconda 4.4.0 (x86_64) चला रहा हूँ।

+9

यह एक बग क्यों होगा? जैसे ही आप पुन: प्रयास करते हैं * आप कुंजी को हटा रहे हैं और जोड़ रहे हैं *। आप भाग्यशाली हैं कि आप अंतहीन पाश में खत्म नहीं हुए। –

+3

* * संग्रह * * पर एक * संग्रह * कभी नहीं बदलें। खासकर शब्दकोश नहीं, क्योंकि इससे अजीब व्यवहार हो सकता है। –

+0

@ user2725109: आप कैसे जानते हैं कि पहला लूप केवल एक बार भाग गया? आप सभी जानते हैं कि यह 1000 बार भाग गया। –

उत्तर

13

नहीं, यह एक बग नहीं है। इस तथ्य को explicitly documented में है:

कुंजी और मूल्यों एक मनमाना आदेश जो गैर यादृच्छिक है, अजगर कार्यान्वयन में भिन्न होती है, और सम्मिलन और विलोपन के शब्दकोश के इतिहास पर निर्भर करता है में से अधिक दोहराया जाता है। यदि चाबियाँ, मूल्यों और वस्तुओं के दृश्यों को शब्दकोश में कोई हस्तक्षेप करने के साथ पुनरावृत्त नहीं किया जाता है, तो वस्तुओं का क्रम सीधे मेल खाता होगा।

[...]

पुनरावृत्ति विचारों जोड़ते या शब्दकोश में प्रविष्टियों को हटाने के लिए एक RuntimeError बढ़ाने या सभी प्रविष्टियों से अधिक तेज़ी से दोहराने में विफल हो सकता है।

बोल्ड जोर मेरा है।

आप कुंजी पर फिर से चल रहे हैं, जबकि साथ ही शब्दकोश में प्रविष्टियों को जोड़ना और हटाना भी शामिल है। यह कुछ पुनरावृत्तियों के लिए काम करता था, और फिर आप सभी प्रविष्टियों मामले में पुनरावृत्ति करने में असफल हो जाते हैं और पुनरावृत्ति बंद हो जाती है।

क्या होता है कि आप 6 जोड़ों पर फिर से आकार को ट्रिगर करते हैं, और इससे उस बिंदु पर पुनरावृत्ति विफल हो जाती है; 'अगली' कुंजी अब 'पहले' स्लॉट में फिसल गई है। यह दोनों परीक्षण के लिए होता है, तो आप सिर्फ यह दोनों ही मामलों में 5 बार iterates एहसास नहीं है:

>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 
>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['i{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 

यह 5 बार क्योंकि वर्तमान dict कार्यान्वयन एक hash table of size 8 के साथ शुरू होता चलाया जाता है, और एक आकार बदलने when the table is 2/3s full शुरू हो रहा है तालिका DKIX_DUMMY entities से भरा जा रहा है (अपने प्रारंभिक dict 5 सम्मिलन यह > (8 * 2/3 == 5.333333) बनाने 1 प्रविष्टि है,।, जब आप एक कुंजी (सही ढंग से हैश टकराव संभालने के लिए आवश्यक) को हटा दें।

ध्यान दें कि यह सब अत्यधिक कार्यान्वयन निर्भर है में प्रवेश किया। पाइथन 3.5 और इससे पहले, दोनों स्निपेट बस फिर से शुरू होते हैं एक बार (भले ही आप कुंजी के लिए सूची ऑब्जेक्ट बनाने से बचने के लिए for k in d: का उपयोग करें); पुनरावृत्ति 3.6 में जारी है क्योंकि कार्यान्वयन बदल गया है और पुनरावृत्ति अब सम्मिलन आदेश का पालन करता है। भविष्य के पायथन संस्करण फिर से कार्यान्वयन को बदलने के लिए स्वतंत्र हैं।

संबंधित मुद्दे