लूप के दौरान सूची प्रविष्टियों को कैसे संशोधित करें?


178

अब मुझे पता है कि पुनरावृति के दौरान सूची को संशोधित करना सुरक्षित नहीं है। हालांकि, मान लीजिए कि मेरे पास स्ट्रिंग्स की एक सूची है, और मैं खुद स्ट्रिंग्स को स्ट्रिप करना चाहता हूं। क्या परिवर्तनशील मानों का प्रतिस्थापन संशोधन के रूप में होता है?


20
एक स्ट्रिंग एक उत्परिवर्तनीय मूल्य नहीं बनाता है।
user470379

4
@ user470379: क्या सूची के तत्व आपस में जुड़े हुए हैं या नहीं, यह सुरक्षित है या नहीं, जबकि यह लूपिंग करते समय सूची को संशोधित करने के लिए प्रासंगिक नहीं है।
21

जवाबों:


144

इसे खराब रूप माना जाता है। यदि आपको सूची के मौजूदा संदर्भों को बनाए रखने की आवश्यकता है, तो स्लाइस असाइनमेंट के बजाय एक सूची समझ का उपयोग करें।

a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)

10
स्लाइस असाइनमेंट चतुर है और लूप के दौरान मूल को संशोधित करने से बचता है, लेकिन एक अस्थायी सूची के निर्माण के लिए मूल की लंबाई की आवश्यकता होती है।
मार्टिन

11
हम b = a क्यों निर्दिष्ट करते हैं?
विगॉनड

9
@ विग्रेड: इसलिए जब print bस्टेटमेंट निष्पादित किया जाता है, तो आप बता सकते हैं कि क्या aबदले हुए स्थान पर संशोधित किया गया था। एक और संभावना यह print b is aदेखने की होगी कि क्या वे दोनों अभी भी एक ही वस्तु को संदर्भित करते हैं।
मार्टीन्यू

12
क्यों [:] = और न सिर्फ एक =?
kdubs

10
@kdubs: "... स्लाइस असाइनमेंट के साथ यदि आपको सूची के मौजूदा संदर्भों को बनाए रखने की आवश्यकता है।"
इग्नासियो वाज़क्वेज़-अब्राम्स

162

चूंकि नीचे का लूप केवल पहले से देखे गए तत्वों को संशोधित करता है, इसलिए इसे स्वीकार्य माना जाएगा:

a = ['a',' b', 'c ', ' d ']

for i, s in enumerate(a):
    a[i] = s.strip()

print(a) # -> ['a', 'b', 'c', 'd']

जो इससे अलग है:

a[:] = [s.strip() for s in a]

इसमें उसे मूल की जगह एक अस्थायी सूची और इसके असाइनमेंट के निर्माण की आवश्यकता नहीं होती है, हालांकि इसके लिए अधिक अनुक्रमण संचालन की आवश्यकता होती है।

सावधानी: हालांकि आप प्रविष्टियों को इस तरह से संशोधित कर सकते हैं , आप listसमस्याओं का सामना करने की संभावना को जोखिम में डाले बिना मदों की संख्या को बदल नहीं सकते हैं ।

यहाँ एक उदाहरण है कि मेरा क्या मतलब है - एक प्रविष्टि को हटाना, उस बिंदु से अनुक्रमण को गड़बड़ाना:

b = ['a', ' b', 'c ', ' d ']

for i, s in enumerate(b):
    if s.strip() != b[i]:  # leading or trailing whitespace?
        del b[i]

print(b)  # -> ['a', 'c ']  # WRONG!

(परिणाम गलत है क्योंकि इसमें उन सभी वस्तुओं को नहीं हटाया गया है जो होनी चाहिए।)

अपडेट करें

चूंकि यह एक काफी लोकप्रिय उत्तर है, यहां प्रविष्टियों को प्रभावी ढंग से "इन-प्लेस" हटाने के लिए है (भले ही यह बिल्कुल सवाल नहीं है):

b = ['a',' b', 'c ', ' d ']

b[:] = [entry for entry in b if entry.strip() == entry]

print(b)  # -> ['a']  # CORRECT

3
for i in aहालांकि सिंटैक्स में अजगर केवल व्यक्तिगत तत्व की एक प्रति क्यों बनाता है ? यह बहुत ही सहज है, अन्य भाषाओं से अलग प्रतीत होता है और मेरे कोड में त्रुटियों के परिणामस्वरूप मुझे लंबे समय तक डिबग करना पड़ा। अजगर ट्यूटोरियल भी इसका उल्लेख नहीं करता है। हालांकि इसके कुछ कारण होने चाहिए?
xji

1
@ जिंकियांग: यह एक प्रतियां नहीं बनाता है। यह सिर्फ लूप वैरिएबल के नाम को उत्तराधिकारी तत्वों या उस चीज़ के मान को निर्दिष्ट करता है, जो इसे पुनरावृत्त करता है।
मार्टीन्यू

1
Eww, एक ही लाइन में एक ही ऑब्जेक्ट के लिए दो नाम ( a[i]और s) का उपयोग क्यों करें जब आपके पास नहीं है? मैं बहुत बल्कि करूँगा a[i] = a[i].strip()
नवीन

3
@Navin: क्योंकि a[i] = s.strip()केवल एक अनुक्रमण ऑपरेशन करता है।
19

1
@martineau enumerate(b)हर पुनरावृत्ति पर एक अनुक्रमण ऑपरेशन करता है और आप एक दूसरे के साथ कर रहे हैं a[i] =। AFAIK लूप पायथन में केवल 1 अनुक्रमण संचालन के साथ पायथन में इस लूप को लागू करना असंभव है :(
नवीन

18

लूप वैरिएंट के लिए एक, एन्यूमरेट () के साथ एक से अधिक क्लीनर दिखता है:

for idx in range(len(list)):
    list[idx]=... # set a new value
    # some other code which doesn't let you use a list comprehension

19
कई लोग range(len(list))पायथन में एक कोड गंध की तरह कुछ का उपयोग करने पर विचार करते हैं ।
मार्टिन

2
@ रीशिन: चूंकि enumerateएक जेनरेटर है, जो लिस्ट नहीं बना रहा है। ट्यूपल्स, यह उन्हें एक बार में बनाता है क्योंकि यह सूची के माध्यम से पुनरावृत्त करता है। यह बताने का एकमात्र तरीका है कि कौन सा धीमा है timeit
मार्टिउ

3
@martineau कोड बहुत अच्छा नहीं हो सकता है, लेकिन timeit enumerateधीमी गति के अनुसार है
Reishin

2
@ रीशिन: आपका बेंचमार्किंग कोड पूरी तरह से मान्य नहीं है क्योंकि यह दिए गए इंडेक्स में सूची में मूल्य को पुनः प्राप्त करने की आवश्यकता को ध्यान में नहीं रखता है - जो इस उत्तर में भी नहीं दिखाया गया है।
मार्टीन्यू

4
@ रीशिन: आपकी तुलना उस कारण से अमान्य है। यह अलगाव में लूपिंग ओवरहेड को माप रहा है। इस समय को पूरा करने में लगने वाले समय को पूरा करने के लिए, इस संभावना के कारण मापा जाना चाहिए कि किसी भी ओवरहेड अंतर को एक निश्चित तरीके से लूप के लूप के अंदर कोड को प्रदान किए गए लाभों से कम किया जा सकता है - अन्यथा आप सेब की तुलना नहीं कर रहे हैं सेब।
मार्टीन्यू

11

किसी सूची को पुनरावृत्ति करते समय प्रत्येक तत्व को संशोधित करना तब तक ठीक है, जब तक आप सूची में तत्वों को जोड़ने / हटाने को नहीं बदलते हैं।

आप सूची बोध का उपयोग कर सकते हैं:

l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]

या सिर्फ C-styleलूप के लिए करें:

for index, item in enumerate(l):
    l[index] = item.strip()

4

नहीं, आप सूची की "सामग्री" में परिवर्तन नहीं करेंगे, यदि आप इस तरह से तार बदल सकते हैं। लेकिन पायथन में वे परस्पर नहीं हैं। कोई भी स्ट्रिंग ऑपरेशन एक नया स्ट्रिंग लौटाता है।

यदि आपके पास उन वस्तुओं की एक सूची थी जिन्हें आप जानते थे कि आप परिवर्तनशील हैं, तो आप ऐसा तब तक कर सकते हैं जब तक आप सूची की वास्तविक सामग्री को नहीं बदलते।

इस प्रकार आपको किसी प्रकार का नक्शा बनाने की आवश्यकता होगी। यदि आप एक जनरेटर अभिव्यक्ति का उपयोग करते हैं तो यह [ऑपरेशन] किया जाएगा जैसा कि आप पुनरावृति करेंगे और आप मेमोरी को बचाएंगे।


4

आप ऐसा कुछ कर सकते हैं:

a = [1,2,3,4,5]
b = [i**2 for i in a]

इसे एक सूची समझ कहा जाता है, जिससे आपको सूची के अंदर लूप करना आसान हो जाता है।


3

Jemshit Iskenderov और Ignacio Vazquez-Abrams द्वारा दिया गया उत्तर वास्तव में अच्छा है। इसे इस उदाहरण के साथ आगे चित्रित किया जा सकता है: कल्पना कीजिए

क) दो वैक्टर के साथ एक सूची आपको दी गई है;

बी) आप सूची को पार करना चाहते हैं और सरणियों में से प्रत्येक के क्रम को उल्टा करना चाहते हैं

मान लीजिए कि आपके पास है

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
    i = i[::-1]   # this command does not reverse the string

print([v,b])

तुम्हे मिल जाएगा

[array([1, 2, 3, 4]), array([3, 4, 6])]

दूसरी ओर, यदि आप करते हैं

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
   i[:] = i[::-1]   # this command reverses the string

print([v,b])

परिणाम है

[array([4, 3, 2, 1]), array([6, 4, 3])]

1

यह आपके प्रश्न से स्पष्ट नहीं है कि स्ट्रिंग्स को हटाने का निर्णय लेने का मापदंड क्या है, लेकिन यदि आपके पास उन स्ट्रिंग्स की सूची है या जिन्हें आप हटाना चाहते हैं, तो आप निम्न कार्य कर सकते हैं:

my_strings = ['a','b','c','d','e']
undesirable_strings = ['b','d']
for undesirable_string in undesirable_strings:
    for i in range(my_strings.count(undesirable_string)):
        my_strings.remove(undesirable_string)

जो my_strings को ['a', 'c', 'e'] में बदलता है


0

संक्षेप में, उसी सूची को पुनरावृत्त करते हुए सूची में संशोधन करना।

list[:] = ["Modify the list" for each_element in list "Condition Check"]

उदाहरण:

list[:] = [list.remove(each_element) for each_element in list if each_element in ["data1", "data2"]]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.