आइए इस कोड पर विचार करें जो एक आइटम को प्रत्येक पुनरावृत्ति से हटाते समय सूची पर पुनरावृति करता है:

x = list(range(5))

for i in x:
    print(i)
    x.pop()

यह 0, 1, 2 प्रिंट करेगा। सूची में अंतिम दो तत्वों को पहले दो पुनरावृत्तियों द्वारा हटा दिए जाने के बाद से केवल पहले तीन तत्व मुद्रित होते हैं।

लेकिन अगर आप तानाशाही पर कुछ इसी तरह की कोशिश करते हैं:

y = {i: i for i in range(5)}

for i in y:
    print(i)
    y.pop(i)

यह 0 प्रिंट करेगा, फिर RuntimeError: dictionary changed size during iteration को बढ़ा देगा, क्योंकि हम डिक्शनरी से एक कुंजी को हटा रहे हैं, जबकि उस पर पुनरावृत्ति कर रहे हैं।

बेशक, पुनरावृत्ति के दौरान सूची को संशोधित करना खराब है। लेकिन RuntimeError को डिक्शनरी की तरह क्यों नहीं उठाया जाता? क्या इस व्यवहार का कोई अच्छा कारण है?

33
ducminh 4 अप्रैल 2018, 11:47

3 जवाब

सबसे बढ़िया उत्तर

मुझे लगता है कि कारण सरल है। lists का आदेश दिया गया है, dicts (पायथन 3.6/3.7 से पहले) और sets नहीं हैं। इसलिए lists को पुनरावृति के रूप में संशोधित करना सर्वोत्तम अभ्यास के रूप में सलाह नहीं दी जा सकती है, लेकिन यह सुसंगत, प्रतिलिपि प्रस्तुत करने योग्य, और गारंटीकृत व्यवहार की ओर ले जाता है।

आप इसका उपयोग कर सकते हैं, उदाहरण के लिए मान लें कि आप एक list को आधे में समान तत्वों के साथ विभाजित करना चाहते हैं और दूसरे भाग को उल्टा करना चाहते हैं:

>>> lst = [0,1,2,3]
>>> lst2 = [lst.pop() for _ in lst]
>>> lst, lst2
([0, 1], [3, 2])

बेशक, इस ऑपरेशन को करने के लिए बहुत बेहतर और अधिक सहज तरीके हैं, लेकिन बात यह है कि यह काम करता है।

इसके विपरीत, dicts और sets के लिए व्यवहार पूरी तरह से कार्यान्वयन विशिष्ट है क्योंकि हैशिंग के आधार पर पुनरावृत्ति क्रम बदल सकता है।

आपको RunTimeError collections.OrderedDict के साथ मिलता है, संभवत: dict व्यवहार के अनुरूप होने के लिए। मुझे नहीं लगता कि पाइथन 3.6 के बाद dict व्यवहार में कोई बदलाव होने की संभावना है (जहां dicts को इंसर्शन ऑर्डर बनाए रखने की गारंटी है) क्योंकि यह बिना किसी वास्तविक उपयोग के मामलों के लिए पिछड़ी संगतता को तोड़ देगा।

ध्यान दें कि आदेश दिए जाने के बावजूद collections.deque इस मामले में RuntimeError भी बढ़ा देता है।

30
Chris_Rands 4 अप्रैल 2018, 12:57

पश्चगामी संगतता को तोड़े बिना सूचियों में इस तरह के चेक को जोड़ना संभव नहीं होता। डिक्ट्स के लिए, ऐसा कोई मुद्दा नहीं था।

पुराने, पूर्व-पुनरावर्तक डिज़ाइन में, for लूप अनुक्रम तत्व पुनर्प्राप्ति हुक को बढ़ते हुए पूर्णांक इंडेक्स के साथ कॉल करके काम करते थे जब तक कि यह इंडेक्स एरर नहीं उठाता। (मैं कहूंगा __getitem__, लेकिन यह टाइप/क्लास एकीकरण से पहले वापस आ गया था, इसलिए C प्रकारों में __getitem__ नहीं था।) len इस डिजाइन में भी शामिल नहीं है, और वहां संशोधन के लिए जाँच करने के लिए कहीं नहीं है।

जब इटरेटर्स को पेश किया गया था, तो डिक्ट इटरेटर के पास से आकार परिवर्तन की जांच थी, जिसने इटरेटर्स को पहली बार पेश किया था। भाषा. इससे पहले डिक्ट्स बिल्कुल भी चलने योग्य नहीं थे, इसलिए तोड़ने के लिए कोई पिछड़ा संगतता नहीं थी। सूचियाँ अभी भी पुराने पुनरावृत्ति प्रोटोकॉल से गुज़री हैं, हालाँकि।

जब list.__iter__ पेश किया गया था, यह पूरी तरह से एक गति अनुकूलन था, इसका इरादा नहीं था एक व्यवहार परिवर्तन हो, और एक संशोधन जांच जोड़ने से पुराने व्यवहार पर निर्भर मौजूदा कोड के साथ पिछड़ी संगतता टूट जाएगी।

8
user2357112 supports Monica 6 मार्च 2019, 20:38

डिक्शनरी एक अतिरिक्त स्तर के संकेत के साथ इंसर्शन ऑर्डर का उपयोग करता है, जो कुंजी को हटाते और फिर से डालने के दौरान हिचकी का कारण बनता है, जिससे डिक्शनरी के ऑर्डर और आंतरिक पॉइंटर्स बदल जाते हैं।

और यह समस्या d के बजाय d.keys() को पुनरावृत्त करने से ठीक नहीं होती है, क्योंकि Python 3 में, d.keys(), dict में चाबियों का एक गतिशील दृश्य देता है जिसके परिणामस्वरूप समान होता है संकट। इसके बजाय, list(d) पर पुनरावृति करें क्योंकि यह शब्दकोश की कुंजियों से एक सूची तैयार करेगा जो पुनरावृत्ति के दौरान नहीं बदलेगा

2
AB Abhi 4 अप्रैल 2018, 09:48