अध्याय 12

लिस्ट — एक ही नाम के तहत कई मान

एक नाम के तहत जितने चाहें उतने मान रखना, इंडेक्स और स्लाइस से अंदर तक पहुँचना, लिस्ट बदलने वाले मेथड — और दो नामों के एक ही लिस्ट से जुड़ने का जाल।

30 मिनटPython 3.12
  1. 1समस्या
  2. 2समझें
  3. 3उदाहरण
  4. 4अनुमान
  5. 5स्वयं करें
  6. 6चुनौती

वह समस्या जिसे हम हल कर रहे हैं

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

python
price_1 = 15
price_2 = 60
price_3 = 850

print(price_1 + price_2 + price_3)
text
925

अब सवाल यह है: चालीस उत्पादों के बारे में क्या? चालीस चर (variables)। और चालीस हिस्सों वाली अंकगणित की एक पंक्ति। और जब दुकान कुछ नया बेचना शुरू करेगी, तो आपको कोड में जाकर एक और चर बनाना पड़ेगा।

समस्या केवल टाइपिंग की नहीं है, यह संरचना की है। पायथन के लिए, price_1, price_2 और price_3 तीन पूरी तरह से असंबंधित नाम हैं — उतने ही असंबंधित जितने कि name और age। आप जानते हैं कि वे एक ही चीज़ के तीन उदाहरण हैं। हमारे पास अभी कोड में "एक ही चीज़ के कई मान" लिखने का कोई तरीका नहीं है।

लिस्ट (List) वही तरीका है। एक नाम, और उसके नीचे जितने चाहें उतने मान, एक क्रम में।

इस मिशन के अंत तक आप सीखेंगे

  • एक लिस्ट बनाना और इंडेक्स तथा स्लाइस के साथ उसके अंदर के मानों तक पहुँचना
  • append, insert, remove और pop के साथ लिस्ट को बदलना
  • यह समझना कि किसी टेक्स्ट (स्ट्रिंग) को क्यों नहीं बदला जा सकता, जबकि लिस्ट को बदला जा सकता है
  • b = a लिखने पर दो नामों के एक ही लिस्ट से जुड़ जाने के जाल को पहचानना और उससे बचना
  • IndexError और ValueError को पढ़ना और यह समझना कि क्या गलत हुआ

पूर्वापेक्षाएँ: for लूप और range.


एक लिस्ट बनाना

चौकोर कोष्ठक (square brackets []), जिसमें मान अल्पविराम (commas) से अलग किए गए हों:

python
prices = [15, 60, 850]

print(prices)
print(len(prices))
text
[15, 60, 850]
3

len() यहाँ बिल्कुल वैसे ही काम करता है जैसे टेक्स्ट पर करता था — तीन मान, इसलिए 3।

प्रिंट करने पर पूरी लिस्ट कोष्ठक सहित दिखाई देती है। यह उपयोगकर्ता के पढ़ने के लिए नहीं है, यह आपके समझने के लिए है: [15, 60, 850] आपको बताता है कि यह एक लिस्ट है, न कि केवल 925।

अंदर के मानों तक पहुँचना

इंडेक्सिंग बिल्कुल वैसे ही काम करती है जैसे स्ट्रिंग्स के लिए करती थी — शून्य से गिनती, और पीछे से ऋणात्मक (negative):

python
prices = [15, 60, 850]

print(prices[0])
print(prices[-1])
print(prices[1:])
text
15
850
[60, 850]

तीनों पंक्तियाँ अध्याय 6 के नियम हैं, बिना किसी बदलाव के। एक अंतर ध्यान देने योग्य है: prices[0] एक संख्या लौटाता है, जबकि prices[1:] एक लिस्ट लौटाता है। एक स्लाइस हमेशा उसी प्रकार की चीज़ लौटाता है जिससे उसे काटा गया था, कभी भी एक अकेला टुकड़ा नहीं।

और यहाँ वास्तविक अंतर है: लिस्ट को बदला जा सकता है

अध्याय 6 में हमने देखा था कि टेक्स्ट के अंदर किसी अक्षर को बदला नहीं जा सकता:

python
name = "rafi"
name[0] = "R"
text
TypeError: 'str' object does not support item assignment

यही पंक्ति लिस्ट पर पूरी तरह काम करती है:

python
prices = [15, 60, 850]
prices[0] = 20
print(prices)
text
[20, 60, 850]

कोई नई लिस्ट नहीं बनी — यही लिस्ट बदल गई। यह एक लिस्ट की सबसे महत्वपूर्ण विशेषता है, और इस अध्याय के अंत में आने वाला जाल सीधे इसी से उत्पन्न होता है।

इसके लिए तकनीकी शब्द: टेक्स्ट अपरिवर्तनीय (immutable) है, लिस्ट परिवर्तनीय (mutable) है।

जोड़ना और हटाना

python
stock = ["pen", "notebook"]

stock.append("bag")
stock.insert(1, "eraser")
print(stock)

stock.remove("pen")
print(stock)

last = stock.pop()
print(last)
print(stock)
text
['pen', 'eraser', 'notebook', 'bag']
['eraser', 'notebook', 'bag']
bag
['eraser', 'notebook']

चार मेथड, चार अलग-अलग काम:

  • append(x) — अंत में जोड़ता है
  • insert(i, x) — इसे स्थिति i पर रखता है, इसके बाद की सभी चीज़ों को दाईं ओर खिसका देता है
  • remove(x) — उस मान वाले पहले आइटम को ढूंढता है और हटा देता है
  • pop() — अंतिम आइटम को हटाता है और उसे वापस लौटाता है

उन अंतिम दो के बीच का अंतर वह है जहाँ अधिकांश भ्रम होता है: remove जानना चाहता है कि क्या हटाना है, pop जानना चाहता है कि कहाँ से हटाना है। और pop आपको हटाई गई चीज़ देता है; remove कुछ नहीं देता।

ये मेथड लिस्ट वापस नहीं लौटाते। टेक्स्ट के साथ आपको name.upper() का परिणाम रखना पड़ता था, क्योंकि उसने नया टेक्स्ट बनाया और वापस लौटाया। लिस्ट इसके विपरीत हैं: append स्वयं लिस्ट को बदल देता है और None लौटाता है।

जिससे यह एक सामान्य गलती बन जाती है:

python
stock = ["pen", "notebook"]
stock = stock.append("bag")
print(stock)
text
None

लिस्ट गायब हो गई। stock.append("bag") लिखें, न कि stock = stock.append("bag")।

खोजना और लूप चलाना

python
stock = ["pen", "notebook", "bag"]

print("pen" in stock)
print("chair" in stock)

for item in stock:
    print(item.upper())
text
True
False
PEN
NOTEBOOK
BAG

in ऑपरेटर टेक्स्ट के अंदर खोज करता था, और यह लिस्ट में भी खोज करता है — वही शब्द वही काम कर रहा है।

और for item in stock एक-एक करके मानों पर चलता है। ध्यान दें कि कोई range नहीं है और कोई इंडेक्स गणित नहीं है।

जब आपको स्थिति (इंडेक्स) और मान दोनों की आवश्यकता हो, तो enumerate:

python
stock = ["pen", "notebook"]
for position, item in enumerate(stock, start=1):
    print(position, item)
text
1 pen
2 notebook

start=1 के बिना गिनती शून्य से शुरू होती है। किसी व्यक्ति को दिखाई जाने वाली सूची आमतौर पर एक से शुरू होनी चाहिए।


वह जाल जो सबको पकड़ लेता है

अध्याय 4 में हमने यह देखा था:

python
a = 3
b = a
b = b + 1
print(a, b)
text
3 4

b को एक मान मिला और वह a को नहीं देख रहा था। अब लिस्ट के साथ वही काम करें:

python
a = [1, 2, 3]
b = a
b.append(4)

print(a)
print(b)
text
[1, 2, 3, 4]
[1, 2, 3, 4]

b बदला गया, और a भी बदल गया।

कारण पहले वाले नियम का खंडन नहीं करता, यह वही नियम है। b = a पंक्ति पर, b को वही मिला जो a के पास था — और जो a के पास था वह था लिस्ट का मेमोरी पता, न कि लिस्ट की प्रतिलिपि। इसलिए दोनों नाम अब एक ही लिस्ट की ओर इशारा करते हैं। b.append(4) ने उस एक लिस्ट को बदल दिया, और दोनों नाम एक ही बदलाव दिखाते हैं।

आप संख्याओं के साथ ऐसा कभी नहीं देखते, क्योंकि b = b + 1 पुरानी संख्या को संशोधित नहीं करता — यह एक नई संख्या बनाता है और उसे b में रखता है। संख्याएँ सुरक्षित हैं क्योंकि उन्हें बदला नहीं जा सकता। लिस्ट बदली जा सकती हैं, इसलिए वे साझा हो जाती हैं।

यदि आप एक अलग प्रतिलिपि चाहते हैं, तो आपको स्पष्ट रूप से माँगना होगा:

python
a = [1, 2, 3]
b = a.copy()
b.append(4)

print(a)
print(b)
text
[1, 2, 3]
[1, 2, 3, 4]
कब सावधान रहना है: जब भी आप किसी लिस्ट को दूसरे नाम के तहत रखें, या किसी लिस्ट को किसी फ़ंक्शन में पास करें (अध्याय 19), तो खुद से एक बार पूछें: क्या मेरा इरादा इसे बदलने का है, या मुझे एक स्वतंत्र प्रतिलिपि चाहिए?

एक संपूर्ण उदाहरण

shopping.py:

python
# एक खरीदारी सूची, बनाई गई और फिर सारांशित की गई
items = []

items.append("pen")
items.append("notebook")
items.append("bag")
items.insert(0, "eraser")

print("List    :", items)
print("Count   :", len(items))
print("First   :", items[0])
print("Last    :", items[-1])
print("Has bag :", "bag" in items)

items.remove("pen")
dropped = items.pop()
print()
print("After removing 'pen' and popping the last one:")
print("List    :", items)
print("Dropped :", dropped)
print("Count   :", len(items))

print()
print("Numbered:")
for position, item in enumerate(items, start=1):
    print(f"  {position}. {item}")
text
List    : ['eraser', 'pen', 'notebook', 'bag']
Count   : 4
First   : eraser
Last    : bag
Has bag : True

After removing 'pen' and popping the last one:
List    : ['eraser', 'notebook']
Dropped : bag
Count   : 2

Numbered:
  1. eraser
  2. notebook

तीन बातें ध्यान देने योग्य हैं:

यह एक खाली लिस्ट से शुरू होता है। पंक्ति items = [] वही काम कर रही है जो अध्याय 10 में लूप के बाहर एक्यूमुलेटर करता था — जहाँ total = 0 था, वहाँ अब [] है। इकट्ठा करना शुरू करने से पहले आपको इकट्ठा करने के लिए एक जगह चाहिए।

insert(0, "eraser") ने इसे सबसे आगे रखा, इसलिए प्रिंट की गई लिस्ट में eraser पहले आता है।

pop() ने जो हटाया वह वापस लौटा दिया, इसलिए dropped में bag है। remove("pen") ने कुछ नहीं लौटाया।


जब यह टूटता है

IndexError: list index out of range लिस्ट में मानों की संख्या से परे एक इंडेक्स। तीन मानों वाली लिस्ट में मान्य इंडेक्स 0, 1, 2 हैं — कोई prices[3] नहीं है। अंतिम मान के लिए, prices[-1] लिखना सबसे सुरक्षित है।

ValueError: list.remove(x): x not in list जिस मान को आपने हटाने के लिए कहा था वह वहाँ नहीं है। पहले if x in stock: से जाँचें, या try का उपयोग करें।

AttributeError: 'NoneType' object has no attribute 'append' किसी पिछली पंक्ति में stock = stock.append(...) लिखा था, इसलिए stock अब None हो चुका है। परिणाम न रखें — अपने आप में केवल stock.append(...) ही पूरा ऑपरेशन है।

मैंने एक लिस्ट बदली और दूसरी भी बदल गई दो नाम एक ही लिस्ट से जुड़े हैं। b = a के बजाय b = a.copy() लिखें।

मैं लूप के अंदर हटा रहा हूँ और आइटम छूट रहे हैं

python
numbers = [1, 2, 2, 3, 4]

for n in numbers:
    if n % 2 == 0:
        numbers.remove(n)

print(numbers)
text
[1, 2, 3]

एक 2 बच गया। जिस लिस्ट पर आप लूप चला रहे हैं उसे छोटा करने से पायथन की गिनती लिस्ट की स्थितियों से बाहर हो जाती है, इसलिए कुछ मान छूट जाते हैं। इसका उपाय हटाना नहीं बल्कि उन चीज़ों को एक नई लिस्ट में इकट्ठा करना है जिन्हें आप रखना चाहते हैं।

python
numbers = [1, 2, 2, 3, 4]
kept = []

for n in numbers:
    if n % 2 != 0:
        kept.append(n)

print(kept)
text
[1, 3]