লিস্ট — একই নামে অনেকগুলো মান
একটি নামে যত খুশি মান রাখা, ইনডেক্স আর স্লাইস দিয়ে ভেতরে পৌঁছানো, আর লিস্ট বদলানোর মেথডগুলো — সাথে দুটি নাম এক লিস্টে লেগে যাওয়ার ফাঁদ।
কীভাবে শিখতে চান?
যে সমস্যাটা আমরা সমাধান করছি
একটা দোকানের তিনটি পণ্যের দাম রাখতে হবে। এতদিন যা শিখেছি তাতে উপায় একটাই:
price_1 = 15
price_2 = 60
price_3 = 850
print(price_1 + price_2 + price_3)925এখন প্রশ্ন: পণ্য চল্লিশটা হলে? চল্লিশটা ভেরিয়েবল। আর যোগ করার লাইনটা চল্লিশ টুকরোর। আর দোকানে একটা নতুন পণ্য এলে কোডে গিয়ে নতুন ভেরিয়েবল বানাতে হবে।
সমস্যাটা শুধু পরিশ্রমের নয়, গঠনেরও। price_1, price_2, price_3 — এই তিনটি নাম পাইথনের কাছে সম্পূর্ণ সম্পর্কহীন, ঠিক যেমন name আর age সম্পর্কহীন। কিন্তু আপনি জানেন এরা একই জিনিসের তিনটি উদাহরণ। সেই “একই জিনিসের অনেকগুলো” ব্যাপারটা কোডে লেখার কোনো উপায় এখনো আমাদের নেই।
লিস্ট সেই উপায়। একটি নাম, তার নিচে যত খুশি মান, ক্রম বজায় রেখে।
এই অধ্যায় শেষে আপনি পারবেন
- লিস্ট বানাতে, আর ইনডেক্স ও স্লাইস দিয়ে তার ভেতরে পৌঁছাতে
append,insert,remove,popদিয়ে লিস্ট বদলাতে- বলতে কেন লিস্ট বদলানো যায় অথচ স্ট্রিং বদলানো যায় না
b = aলিখলে দুটি নাম একই লিস্টে লেগে যায় — এটা চিনতে ও এড়াতেIndexErrorআরValueErrorপড়ে বুঝতে কী ভুল হয়েছে
আগে যা জানা লাগবে: for লুপ আর range।
একটি লিস্ট বানানো
স্কয়ার ব্র্যাকেট, আর ভেতরে কমা দিয়ে আলাদা করা মান:
prices = [15, 60, 850]
print(prices)
print(len(prices))[15, 60, 850]
3len() এখানেও কাজ করে, ঠিক স্ট্রিং-এর মতোই — তিনটি মান, তাই 3।
ছাপালে স্কয়ার ব্র্যাকেটসহ পুরো লিস্টটাই দেখা যায়। এটা মানুষের পড়ার জন্য নয়, বোঝার জন্য: [15, 60, 850] দেখে বোঝা যায় এটি একটি লিস্ট, 925 নয়।
ভেতরে পৌঁছানো
স্ট্রিং-এর ইনডেক্স যেভাবে কাজ করত, ঠিক সেভাবেই — শূন্য থেকে শুরু, পিছন থেকে নেগেটিভ:
prices = [15, 60, 850]
print(prices[0])
print(prices[-1])
print(prices[1:])15
850
[60, 850]তিনটি লাইনের তিনটিই ৬ নম্বর অধ্যায়ের নিয়ম, নতুন কিছু নয়। একটাই পার্থক্য খেয়াল করার মতো: prices[0] একটি সংখ্যা দেয়, কিন্তু prices[1:] একটি লিস্ট দেয় — স্লাইস সবসময় একই ধরনের জিনিস ফেরত দেয়, একটি টুকরো নয়।
এবার আসল পার্থক্য: লিস্ট বদলানো যায়
৬ নম্বর অধ্যায়ে দেখেছিলাম স্ট্রিং-এর ভেতরের একটি অক্ষর বদলানো যায় না:
name = "rafi"
name[0] = "R"TypeError: 'str' object does not support item assignmentলিস্টে ঠিক একই লাইন কাজ করে:
prices = [15, 60, 850]
prices[0] = 20
print(prices)[20, 60, 850]কোনো নতুন লিস্ট তৈরি হয়নি — এই লিস্টটাই বদলে গেছে। এটিই লিস্টের সবচেয়ে গুরুত্বপূর্ণ বৈশিষ্ট্য, আর এই অধ্যায়ের শেষ অংশের ফাঁদটাও এখান থেকেই আসে।
পরিভাষায়: স্ট্রিং immutable (অপরিবর্তনীয়), লিস্ট mutable (পরিবর্তনীয়)।
যোগ করা আর বাদ দেওয়া
stock = ["pen", "notebook"]
stock.append("bag")
stock.insert(1, "eraser")
print(stock)
stock.remove("pen")
print(stock)
last = stock.pop()
print(last)
print(stock)['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।
তাই এই লাইনটি একটি ক্লাসিক ভুল:
stock = ["pen", "notebook"]
stock = stock.append("bag")
print(stock)Noneলিস্টটা হারিয়ে গেল। stock.append("bag") লিখুন, stock = stock.append("bag") নয়।
খোঁজা আর ঘোরা
stock = ["pen", "notebook", "bag"]
print("pen" in stock)
print("chair" in stock)
for item in stock:
print(item.upper())True
False
PEN
NOTEBOOK
BAGin স্ট্রিং-এর ভেতরে খুঁজত, লিস্টেও খোঁজে — একই শব্দ, একই কাজ।
আর for item in stock লিস্টের প্রতিটি মানের উপর দিয়ে হাঁটে। খেয়াল করুন এখানে range লাগেনি, ইনডেক্সের হিসাবও করতে হয়নি — ১১ নম্বর অধ্যায়ে স্ট্রিং-এর অক্ষরের উপর যেভাবে হেঁটেছিলাম, হুবহু সেভাবে।
অবস্থান (ইনডেক্স) আর মান দুটোই দরকার হলে enumerate:
stock = ["pen", "notebook"]
for position, item in enumerate(stock, start=1):
print(position, item)1 pen
2 notebookstart=1 না দিলে গণনা শূন্য থেকে শুরু হয়। মানুষকে দেখানোর তালিকায় সাধারণত এক থেকেই শুরু করতে হয়।
যে ফাঁদটা সবচেয়ে বেশি ভোগায়
৪ নম্বর অধ্যায়ে দেখেছিলাম:
a = 3
b = a
b = b + 1
print(a, b)3 4b মান পেয়েছিল, a-এর দিকে তাকিয়ে ছিল না। এখন লিস্টে একই জিনিস করে দেখুন:
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)[1, 2, 3, 4]
[1, 2, 3, 4]b বদলানো হয়েছিল, কিন্তু a-ও বদলে গেছে।
কারণটা আগের নিয়মের বিরোধী নয়, বরং ঠিক সেটাই। b = a লাইনে b পেয়েছে a-তে যা ছিল তা-ই — আর a-তে ছিল লিস্টটির অবস্থান, লিস্টটির একটি কপি নয়। তাই দুটি নাম এখন একই লিস্টের দিকে দেখাচ্ছে। b.append(4) সেই একটিমাত্র লিস্টকেই বদলেছে, আর দুটি নাম দিয়েই সেই একই বদল দেখা যাচ্ছে।
সংখ্যায় এটা চোখে পড়ে না, কারণ b = b + 1 লাইনটি পুরোনো সংখ্যাটা বদলায় না — সে একটি নতুন সংখ্যা বানিয়ে b-তে বসায়। বদলাতে পারে না বলেই সংখ্যা নিরাপদ। লিস্ট বদলাতে পারে, তাই লিস্ট নয়।
আলাদা একটা কপি চাইলে চেয়ে নিতে হবে:
a = [1, 2, 3]
b = a.copy()
b.append(4)
print(a)
print(b)[1, 2, 3]
[1, 2, 3, 4]কখন সতর্ক হবেন। যখনই একটি লিস্ট অন্য নামে বসাচ্ছেন, বা একটি লিস্ট কোনো ফাংশনে পাঠাচ্ছেন (১৯ নম্বর অধ্যায়), একবার জিজ্ঞেস করুন: আমি কি এটাকেই বদলাতে চাই, নাকি একটা কপি চাই? এই প্রশ্নটা যারা করে না, তারা দিনের পর দিন খুঁজে বেড়ায় “আমি তো এখানে কিছু বদলাইনি” জাতীয় বাগ।
একটা সম্পূর্ণ উদাহরণ
shopping.py:
# A shopping list, built up and then summarised
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}")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 = [] লাইনটি লুপের বাইরের অ্যাকিউমুলেটরের মতোই — ১০ নম্বর অধ্যায়ে total = 0 যে কাজটা করত, এখানে [] সেটাই করছে। জমা করার আগে জমা করার জায়গাটা থাকতে হয়।
insert(0, "eraser") সবার আগে বসিয়েছে, তাই ছাপা তালিকায় eraser প্রথম — যোগ করার ক্রম আর তালিকার ক্রম এক নয়।
pop() বাদ দেওয়া জিনিসটা ফেরত দিয়েছে, তাই dropped-এ bag ধরা আছে। remove("pen") কিছু ফেরত দেয়নি, তাই বাদ দেওয়া 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() লিখুন।
লুপের ভেতরে বাদ দিচ্ছি, কিছু বাদ পড়ে যাচ্ছে
numbers = [1, 2, 2, 3, 4]
for n in numbers:
if n % 2 == 0:
numbers.remove(n)
print(numbers)[1, 2, 3]একটি 2 টিকে গেছে। যে লিস্টের উপর দিয়ে হাঁটছেন সেটিকেই ছোট করলে পাইথনের গণনা আর লিস্টের অবস্থান মিলে না, তাই কিছু মান এড়িয়ে যায়। সমাধান: বাদ না দিয়ে নতুন একটি লিস্টে রাখুন যেগুলো রাখতে চান।
numbers = [1, 2, 2, 3, 4]
kept = []
for n in numbers:
if n % 2 != 0:
kept.append(n)
print(kept)[1, 3]list নামে ভেরিয়েবল বানিয়েছি, এখন list(...) কাজ করছে না list পাইথনের নিজের নাম। items, prices, names — অর্থপূর্ণ নাম দিন।
ধাপ ৪ / ৬ — অনুমান
যাচাই করুন
append-এর ফলটি আবার stock-এ বসানো হয়েছে। কী ছাপা হবে?
stock = ["pen", "notebook"]
stock = stock.append("bag")
print(stock)- ANone
- B['pen', 'notebook', 'bag']
- C['bag']
- Dএকটি `AttributeError`
শুধু b বদলানো হয়েছে। দুটি লাইন কী ছাপবে?
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)- A[1, 2, 3] [4]
- B[1, 2, 3] [1, 2, 3, 4]
- C[1, 2, 3, 4] [1, 2, 3]
- D[1, 2, 3, 4] [1, 2, 3, 4]
লিস্টে তিনটি মান আছে, আর তিন নম্বর ইনডেক্স চাওয়া হয়েছে। কী হবে?
prices = [15, 60, 850]
print(prices[3])- Aএকটি খালি লিস্ট
- B`IndexError` — তিনটি মানের বৈধ ইনডেক্স `0`, `1`, `2`
- CNone
- D850 — শেষ মানটি
উত্তর দিতে অ্যাকাউন্ট লাগবে
উত্তর মিলিয়ে দেখতে সাইন ইন করুন
প্রশ্নগুলো উপরে আছে, আর মাথায় মাথায় উত্তর ভেবে নেওয়াই আসল কাজ। সঠিক উত্তর, ব্যাখ্যা আর তিন ধাপের ইঙ্গিত দেখতে সাইন ইন করুন।
নিজে করুন
marks.py নামে একটি ফাইল লিখুন, যেখানে অন্তত ছয়জনের নম্বরের একটি লিস্ট থাকবে।
তারপর ছাপান:
- পুরো লিস্ট, আর তাতে কয়টি নম্বর আছে
- প্রথম ও শেষ নম্বর — শেষটির জন্য
[-1]ব্যবহার করুন - যোগফল আর গড় (লুপ দিয়ে জমা করুন, হাতে লিখবেন না)
enumerateদিয়ে এক থেকে শুরু করা একটি নম্বরযুক্ত তালিকা- পাশ করা নম্বরগুলোর (৪০ বা বেশি) একটি নতুন লিস্ট, আর তাতে কয়টি আছে
তারপর দুটো পরীক্ষা করুন, আর দুটোই গুরুত্বপূর্ণ:
- লিস্টের শেষে একটি নম্বর
appendকরুন, তারপর আবার যোগফল ছাপান। যোগফলের লাইনটা কি বদলাতে হলো? না হলে আপনি সত্যিকারের হিসাব লিখেছেন। backup = marksলিখে তারপরmarks.append(100)করুন, আর দুটোই ছাপান।backup-এ কী দেখছেন? এবার.copy()দিয়ে একই কাজ করে পার্থক্যটা দেখুন।
শেষ পরীক্ষাটা এই অধ্যায়ের সবচেয়ে দামি তিন মিনিট। এটা নিজের চোখে একবার দেখলে পরে আর ধরতে সময় লাগবে না।
ধাপ ৬ / ৬
কঠিন করা — অধ্যায়ের কুইজ
সহজ থেকে কঠিন — দশটি প্রশ্ন, শেষেরগুলো ইচ্ছে করেই কঠিন।
সাইন ইন করে কুইজ দিন