सूची में परिवर्तन की सूची अप्रत्याशित रूप से उपनलियों में परिलक्षित होती है


643

मुझे पायथन में सूचियों की सूची बनाने की आवश्यकता थी, इसलिए मैंने निम्नलिखित टाइप किया:

myList = [[1] * 4] * 3

सूची इस प्रकार दिखी:

[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]  

फिर मैंने एक अंतरतम मूल्यों को बदल दिया:

myList[0][0] = 5

अब मेरी सूची इस प्रकार है:

[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]  

वह नहीं है जो मैं चाहता था या अपेक्षित था। क्या कोई कृपया बता सकता है कि क्या चल रहा है, और इसे कैसे प्राप्त करें?

जवाबों:


558

जब आप लिखते हैं [x]*3तो आपको अनिवार्य रूप से सूची मिलती है [x, x, x]। अर्थात्, 3 संदर्भों वाली एक सूची x। जब आप इस एकल को संशोधित करते हैं तो यह उसके xतीन संदर्भों के माध्यम से दिखाई देता है:

x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
    f"id(l[0]): {id(l[0])}\n"
    f"id(l[1]): {id(l[1])}\n"
    f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048

x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]

इसे ठीक करने के लिए, आपको यह सुनिश्चित करने की आवश्यकता है कि आप प्रत्येक स्थान पर एक नई सूची बनाएँ। इसे करने का एक तरीका है

[[1]*4 for _ in range(3)]

जो [1]*4एक बार मूल्यांकन करने और 1 सूची में 3 संदर्भ बनाने के बजाय प्रत्येक बार पुनर्मूल्यांकन करेगा ।


आप आश्चर्यचकित हो सकते हैं कि *सूची को समझने के लिए स्वतंत्र वस्तुएं क्यों नहीं बना सकती हैं। ऐसा इसलिए है क्योंकि गुणन ऑपरेटर *भावों को देखे बिना वस्तुओं पर काम करता है। जब आप 3 *से गुणा करने के लिए उपयोग करते हैं [[1] * 4], तो *केवल 1-तत्व सूची का [[1] * 4]मूल्यांकन करता है, न कि [[1] * 4अभिव्यक्ति पाठ को देखता है । *उस तत्व की प्रतियां बनाने का कोई विचार नहीं है, कोई विचार नहीं है कि कैसे पुनर्मूल्यांकन किया जाए [[1] * 4], और कोई भी विचार जिसे आप प्रतियां भी नहीं चाहते हैं, और सामान्य रूप से, तत्व की प्रतिलिपि बनाने का एक तरीका भी नहीं हो सकता है।

एकमात्र विकल्प *यह है कि नए उपशीर्षकों को बनाने की कोशिश करने के बजाय मौजूदा सबलिस्ट में नए संदर्भ बनाएं। कुछ भी असंगत होगा या मौलिक भाषा डिजाइन निर्णयों के प्रमुख पुनर्निर्देशन की आवश्यकता होगी।

इसके विपरीत, एक सूची समझ हर पुनरावृत्ति पर तत्व अभिव्यक्ति को पुनर्मूल्यांकित करती है। हर बार [[1] * 4 for n in range(3)]reevaluates [1] * 4एक ही कारण के लिए हर बार [x**2 for x in range(3)]reevaluates x**2। हर मूल्यांकन [1] * 4एक नई सूची तैयार करता है, इसलिए सूची की समझ वही है जो आप चाहते थे।

संयोग से, [1] * 4के तत्वों को भी कॉपी नहीं करता है [1], लेकिन यह मायने नहीं रखता है, क्योंकि पूर्णांक अपरिवर्तनीय हैं। आप ऐसा कुछ नहीं कर सकते 1.value = 2और 1 को 2 में बदल सकते हैं ।


24
मुझे आश्चर्य है कि कोई भी निकाय यह नहीं बताता है कि, यहाँ उत्तर भ्रामक है। [x]*33 संदर्भों को स्टोर करें जैसे [x, x, x]कि केवल तभी सही होता xहै जब परस्पर परिवर्तन किया जाता है। यह उदाहरण के लिए a=[4]*3, जहाँ a[0]=5, के बाद के लिए काम नहीं करता है ,a=[5,4,4].
एलनकुंजी

42
तकनीकी रूप से, यह अभी भी सही है। [4]*3अनिवार्य रूप से के बराबर है x = 4; [x, x, x]। हालांकि, यह सच है, क्योंकि इससे कोई समस्या नहीं होगी क्योंकि 4यह अपरिवर्तनीय है। साथ ही, आपका अन्य उदाहरण वास्तव में एक अलग मामला नहीं है। a = [x]*3; a[0] = 5यदि xआप संशोधित नहीं कर रहे हैं, तो भी समस्याएँ उत्पन्न नहीं होंगी , क्योंकि आप xकेवल संशोधन कर रहे हैं a। मैं अपने जवाब को भ्रामक या गलत नहीं बताऊंगा - यदि आप अपरिवर्तनीय वस्तुओं के साथ काम कर रहे हैं तो आप अपने आप को पैर में गोली नहीं मार सकते
कैडकेर

19
@Allanqunzi तुम गलत हो। Do x = 1000; lst = [x]*2; lst[0] is lst[1]-> True। अजगर यहाँ परस्पर और अपरिवर्तनीय वस्तुओं के बीच अंतर नहीं करता है।
टाइमजैब

128
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]

तख्ते और वस्तुएँ

लाइव पायथन ट्यूटर विज़ुअलाइज़


इसलिए, अगर हम मैट्रिक्स लिखते हैं = [[x] * २] एक ही वस्तु के लिए २ हाथी नहीं बनाते हैं, जैसे उदाहरण आप वर्णन करते हैं, तो यह एक ही अवधारणा प्रतीत होती है, मैं क्या याद कर रहा हूँ?
अहमद मोहम्मद

@ अहमद मोहम्मद वास्तव में यह एक ही वस्तु के दो तत्वों के साथ एक सूची बनाता है जिसे xसंदर्भित करता है। यदि आप विश्व स्तर पर अद्वितीय वस्तु x = object()बनाते हैं और फिर बनाते हैं तो यह matrix = [[x] * 2]सच होता है:matrix[0][0] is matrix[0][1]
nadrimajstor

@nadrimajstor इसलिए मैट्रिक्स में परिवर्तन [0] मैट्रिक्स को प्रभावित नहीं करता है [1] जैसा कि 2d मैट्रिक्स के साथ ऊपर उदाहरण है।
अहमद मोहम्मद

@ अहमद आश्चर्य तब आता है जब आप परस्पर अनुक्रम की एक "प्रतिलिपि" बनाते हैं (हमारे उदाहरण में यह एक है list) तो अगर एक row = [x] * 2से अधिक matrix = [row] * 2जहां दोनों पंक्तियाँ बिल्कुल एक ही वस्तु हैं, और अब एक पंक्ति में परिवर्तन matrix[0][0] = yअचानक दूसरे में दिखाई देता है(matrix[0][0] is matrix[1][0]) == True
nadrimajstor

@AhmedMohamed नेड बैचेल्ड पर एक नज़र डालें - पायथन के नामों और मूल्यों के बारे में तथ्य और मिथक क्योंकि यह एक बेहतर व्याख्या प्रस्तुत कर सकता है। :)
nadrimajstor

52

वास्तव में, यह वही है जो आप उम्मीद करेंगे। चलो यहाँ क्या हो रहा है:

तुम लिखो

lst = [[1] * 4] * 3

यह इसके बराबर है:

lst1 = [1]*4
lst = [lst1]*3

इसका मतलब lstहै एक सूची जिसमें 3 तत्व हैं जो सभी को इंगित करते हैं lst1। इसका मतलब है कि निम्नलिखित दो पंक्तियाँ समान हैं:

lst[0][0] = 5
lst1[0] = 5

जैसा lst[0]कि कुछ भी नहीं है lst1

वांछित व्यवहार प्राप्त करने के लिए, आप सूची समझ का उपयोग कर सकते हैं:

lst = [ [1]*4 for n in range(3) ] #python 3
lst = [ [1]*4 for n in xrange(3) ] #python 2

इस स्थिति में, प्रत्येक n के लिए अभिव्यक्ति का पुनर्मूल्यांकन किया जाता है, जिससे एक अलग सूची बनती है।


यहाँ अच्छा जवाब के लिए बस एक छोटा सा जोड़: यह स्पष्ट है कि आप एक ही वस्तु के साथ काम कर रहे हैं यदि आप id(lst[0][0])और id(lst[1][0])भी याid(lst[0])id(lst[1])
Sergiy Kolodyazhnyy

36
[[1] * 4] * 3

या और भी:

[[1, 1, 1, 1]] * 3

एक सूची बनाता है जो आंतरिक [1,1,1,1]3 बार संदर्भित करता है - आंतरिक सूची की तीन प्रतियां नहीं, इसलिए जब भी आप सूची को संशोधित करते हैं (किसी भी स्थिति में), तो आप तीन बार परिवर्तन देखेंगे।

यह इस उदाहरण के समान है:

>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

जहां यह शायद थोड़ा कम आश्चर्य की बात है।


3
आप इसे खोजने के लिए "is" ऑपरेटर का उपयोग कर सकते हैं। ls [0] ls [1] रिटर्न ट्रू है।
मियादी

9

स्वीकार किए जाते हैं जवाब यह है कि, सही ढंग से समस्या बताया गया है, अपनी सूची समझ के भीतर यदि आप अजगर-2.x उपयोग का उपयोग कर रहे के साथ xrange()कि रिटर्न एक जनरेटर जो अधिक कुशल है ( range()अजगर 3 में एक ही काम करता है) _throwaway चर के बजाय n:

[[1]*4 for _ in xrange(3)]      # and in python3 [[1]*4 for _ in range(3)]

इसके अलावा, एक बहुत अधिक पायथोनिक तरीके के रूप में आप itertools.repeat()दोहराए गए तत्वों के एक पुनरावृत्त वस्तु बनाने के लिए उपयोग कर सकते हैं :

>>> a=list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0]=5
>>> a
[5, 1, 1, 1]

पुनश्च numpy का उपयोग करना, अगर आप केवल लोगों को या शून्य आप उपयोग कर सकते हैं की एक सरणी बनाना चाहते हैं np.onesऔर np.zerosऔर / या अन्य नंबर इस्तेमाल के लिए np.repeat():

In [1]: import numpy as np

In [2]: 

In [2]: np.ones(4)
Out[2]: array([ 1.,  1.,  1.,  1.])

In [3]: np.ones((4, 2))
Out[3]: 
array([[ 1.,  1.],
       [ 1.,  1.],
       [ 1.,  1.],
       [ 1.,  1.]])

In [4]: np.zeros((4, 2))
Out[4]: 
array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.]])

In [5]: np.repeat([7], 10)
Out[5]: array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])

6

पायथन कंटेनर में अन्य वस्तुओं के संदर्भ होते हैं। इस उदाहरण को देखें:

>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]

इसमें bएक सूची है जिसमें एक आइटम है जो सूची का संदर्भ है a। सूची aपरस्पर है।

किसी पूर्णांक द्वारा किसी सूची का गुणन कई बार सूची को स्वयं में जोड़ने के बराबर है ( सामान्य अनुक्रम संचालन देखें )। तो उदाहरण के साथ जारी है:

>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]

हम देख सकते हैं कि सूची में cअब सूची के दो संदर्भ हैं aजो इसके बराबर है c = b * 2

पायथन एफएक्यू में इस व्यवहार की व्याख्या भी शामिल है: मैं एक बहुआयामी सूची कैसे बना सकता हूं?


6

myList = [[1]*4] * 3[1,1,1,1]स्मृति में एक सूची ऑब्जेक्ट बनाता है और 3 बार उसके संदर्भ की प्रतिलिपि बनाता है। इसके बराबर है obj = [1,1,1,1]; myList = [obj]*3। सूची में objजहां कहीं भी objसंदर्भित है , किसी भी संशोधन को तीन स्थानों पर प्रतिबिंबित किया जाएगा । सही कथन होगा:

myList = [[1]*4 for _ in range(3)]

या

myList = [[1 for __ in range(4)] for _ in range(3)]

यहां ध्यान देने वाली महत्वपूर्ण बात यह है कि *ऑपरेटर का उपयोग अधिकतर शाब्दिकों की सूची बनाने के लिए किया जाता है । हालांकि 1अपरिवर्तनीय है, obj =[1]*4फिर भी बनाने के लिए 1बार-बार 4 बार की सूची बनाएंगे [1,1,1,1]। लेकिन यदि किसी अपरिवर्तनीय वस्तु का संदर्भ दिया जाता है, तो वस्तु को एक नए के साथ अधिलेखित कर दिया जाता है।

इसका मतलब है कि अगर हम ऐसा करेंगे obj[1]=42, तो ऐसा नहींobj होगा जैसा कि कुछ मान सकते हैं। यह भी सत्यापित किया जा सकता है:[1,42,1,1] [42,42,42,42]

>>> myList = [1]*4
>>> myList
[1, 1, 1, 1]

>>> id(myList[0])
4522139440
>>> id(myList[1]) # Same as myList[0]
4522139440

>>> myList[1] = 42 # Since myList[1] is immutable, this operation overwrites myList[1] with a new object changing its id.
>>> myList
[1, 42, 1, 1]

>>> id(myList[0])
4522139440
>>> id(myList[1]) # id changed
4522140752
>>> id(myList[2]) # id still same as myList[0], still referring to value `1`.
4522139440

2
यह शाब्दिक के बारे में नहीं है। इंडेक्स पर obj[2] = 42 संदर्भ को बदल देता है2 , जैसा कि उस इंडेक्स द्वारा संदर्भित ऑब्जेक्ट को म्यूट करने के विरोध में होता है, जो ऐसा myList[2][0] = ...करता है ( myList[2]एक सूची है, और एसिगमेंट tha सूची में इंडेक्स 0 पर संदर्भ को बदल देता है)। बेशक, पूर्णांक परस्पर नहीं हैं, लेकिन ऑब्जेक्ट के बहुत सारे प्रकार हैं । और ध्यान दें कि [....]सूची प्रदर्शन संकेतन भी शाब्दिक वाक्य रचना का एक रूप है! मिश्रित बनाम अपरिवर्तनीय वस्तुओं के साथ मिश्रित (जैसे सूचियों) और अदिश वस्तुओं (जैसे पूर्णांक) को भ्रमित न करें।
मार्टिजन पीटरर्स

5

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

अपनी समस्या को हल करने के लिए आप उनमें से किसी एक को कर सकते हैं: 1. numpy.empty के लिए numpy सरणी दस्तावेज़ीकरण का उपयोग करें। सूची में प्राप्त करने के साथ ही सूची में जोड़ें। 3. आप चाहें तो डिक्शनरी का भी इस्तेमाल कर सकते हैं


2

हम निम्नलिखित तरीके से आपके कोड को फिर से लिखते हैं:

x = 1
y = [x]
z = y * 4

myList = [z] * 3

इसके बाद, सब कुछ अधिक स्पष्ट करने के लिए निम्न कोड चलाएँ। कोड क्या करता है मूल रूप idसे प्राप्त वस्तुओं के एस प्रिंट करता है , जो

किसी वस्तु की "पहचान" लौटाएं

और हमें उनकी पहचान करने और क्या होता है का विश्लेषण करने में मदद करेगा:

print("myList:")
for i, subList in enumerate(myList):
    print("\t[{}]: {}".format(i, id(subList)))
    for j, elem in enumerate(subList):
        print("\t\t[{}]: {}".format(j, id(elem)))

और आपको निम्न आउटपुट मिलेगा:

x: 1
y: [1]
z: [1, 1, 1, 1]
myList:
    [0]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [1]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [2]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528

तो अब हम चरण-दर-चरण चलते हैं। आपके पास xजो 1है, और एक भी तत्व सूची yसे युक्त x। आपका पहला कदम है y * 4जो आपको एक नई सूची देगा z, जो मूल रूप से है [x, x, x, x], अर्थात यह एक नई सूची बनाता है जिसमें 4 तत्व होंगे, जो प्रारंभिक xवस्तु के संदर्भ हैं । शुद्ध कदम बहुत समान है। आप मूल रूप से करते हैं z * 3, जो कि है [[x, x, x, x]] * 3और रिटर्न [[x, x, x, x], [x, x, x, x], [x, x, x, x]], उसी कारण के लिए जैसे पहले चरण के लिए।


2

मुझे लगता है कि हर कोई समझा रहा है कि क्या हो रहा है। मैं इसे हल करने का एक तरीका सुझाता हूं:

myList = [[1 for i in range(4)] for j in range(3)]

myList[0][0] = 5

print myList

और फिर आपके पास है:

[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]

2

इसे और अधिक स्पष्ट रूप से समझाने की कोशिश कर रहा है,

ऑपरेशन 1:

x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]

x[0][0] = 1
print(x) # [[1, 0], [0, 0]]

ऑपरेशन 2:

y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [1, 0]]

ध्यान दिया जाता है कि पहली सूची के पहले तत्व को संशोधित करना प्रत्येक सूची के दूसरे तत्व को संशोधित क्यों नहीं करता है? ऐसा इसलिए है क्योंकि [0] * 2वास्तव में दो नंबरों की एक सूची है, और 0 के संदर्भ को संशोधित नहीं किया जा सकता है।

यदि आप क्लोन प्रतियां बनाना चाहते हैं, तो ऑपरेशन 3 का प्रयास करें:

import copy
y = [0] * 2   
print(y)   # [0, 0]

y = [y, copy.deepcopy(y)]  
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [0, 0]]

क्लोन कॉपी बनाने का एक और दिलचस्प तरीका, ऑपरेशन 4:

import copy
y = [0] * 2
print(y) # [0, 0]

y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]

y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]

2

पायथन सूची गुणन से @spelchekr : [[...]] * 3 3 सूचियाँ बनाता है जो एक दूसरे को संशोधित करते समय आईना बनाती हैं और मेरा भी यही प्रश्न था कि "केवल बाहरी * 3 ही अधिक संदर्भ क्यों बनाता है जबकि आंतरिक एक नहीं करता है" "यह सब 1s क्यों नहीं है?"

li = [0] * 3
print([id(v) for v in li]) # [140724141863728, 140724141863728, 140724141863728]
li[0] = 1
print([id(v) for v in li]) # [140724141863760, 140724141863728, 140724141863728]
print(id(0)) # 140724141863728
print(id(1)) # 140724141863760
print(li) # [1, 0, 0]

ma = [[0]*3] * 3 # mainly discuss inner & outer *3 here
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
ma[0][0] = 1
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
print(ma) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

ऊपर दिए गए कोड को आज़माने के बाद यहाँ मेरा स्पष्टीकरण दिया गया है:

  • भीतर *3भी संदर्भ बनाता है, लेकिन यह संदर्भ अपरिवर्तनीय है, जैसे कुछ [&0, &0, &0], फिर जब बदलना है , तो आप li[0]कॉन्स्टेंट इंट के किसी भी अंतर्निहित संदर्भ को नहीं बदल सकते हैं 0, इसलिए आप संदर्भ पते को नए में बदल सकते हैं &1;
  • जबकि ma=[&li, &li, &li]और li, परिवर्तनशील है, इसलिए जब आप कॉल ma[0][0]=1, मा [0] [0] समान रूप से करने के लिए है &li[0], इसलिए सभी &liउदाहरणों में अपनी 1st पता बदल जाएगा &1

1

इनबिल्ट सूची फ़ंक्शन का उपयोग करके आप इस तरह कर सकते हैं

a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list

a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number

a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting

a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list

1
ध्यान दें, यदि आप दूसरा चरण बनाते हैं, तो चौथा चरण गिराया जा सकता है:a.insert(0,[5,1,1,1])
U10- फ़ॉरवर्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.