ডিকশনারি — key আর ভ্যালুর জোড়া
নাম ধরে ভ্যালু রাখা ও তোলা, KeyError এড়াতে .get(), key যোগ-বদল-বাদ, .items() দিয়ে ঘোরা, আর কেন লিস্ট key হতে পারে না।
কীভাবে শিখতে চান?
যে সমস্যাটা আমরা সমাধান করছি
তিনজনের নম্বর রাখতে হবে — কার কত, সেটাসহ।
লিস্ট দিয়ে একটাই উপায়: দুটো লিস্ট, পাশাপাশি।
names = ["rafi", "ahmed", "bilal"]
marks = [72, 45, 90]
print(names[1], marks[1])ahmed 45কাজ করছে। কিন্তু এই ব্যবস্থাটা সম্পূর্ণ আপনার শৃঙ্খলার উপর দাঁড়িয়ে আছে, আর পাইথনের কোনো সাহায্য এখানে নেই।
names-এ একজনকে যোগ করে marks-এ ভুলে গেলে? দুটো লিস্টের দৈর্ঘ্য আলাদা হয়ে যায়, আর তারপর names[2] আর marks[2] দুজন আলাদা মানুষের কথা বলে। পাইথন কিছুই বলবে না। names সাজালে? সম্পর্কটা পুরোপুরি ভেঙে যায়, নীরবে।
সমস্যাটার গোড়া এখানে: দুটি জিনিসের মধ্যে সম্পর্কটা কোথাও লেখা নেই। সম্পর্কটা কেবল “একই ইনডেক্স” — একটা কাকতাল, যা যেকোনো মুহূর্তে ভেঙে যেতে পারে।
ডিকশনারি সম্পর্কটাকে কোডে লিখে ফেলে। ইনডেক্সের বদলে নাম, আর জোড়াটা এক জায়গায় বাঁধা।
এই অধ্যায় শেষে আপনি পারবেন
- ডিকশনারি বানাতে, আর key দিয়ে ভ্যালু তুলতে
KeyErrorএড়াতে.get()ব্যবহার করতে, আর কখন কোনটা ঠিক জানতে- key যোগ করতে, বদলাতে আর বাদ দিতে
.keys(),.values(),.items()দিয়ে ঘুরতে- বলতে কেন লিস্ট key হতে পারে না, কিন্তু টাপল পারে
আগে যা জানা লাগবে: টাপল আর আনপ্যাকিং।
ডিকশনারি — key আর ভ্যালুর জোড়া
কার্লি ব্র্যাকেট, আর ভেতরে key: value জোড়া:
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(marks)
print(marks["ahmed"])
print(len(marks)){'rafi': 72, 'ahmed': 45, 'bilal': 90}
45
3marks["ahmed"] — ইনডেক্স নয়, নাম। আর নামটাই কোডে লেখা আছে, তাই ছয় মাস পরেও পড়লে বোঝা যায় কী চাওয়া হচ্ছে।
len() জোড়ার সংখ্যা গোনে, তিনটা।
ক্রম নিয়ে একটি কথা। পাইথন ৩.৭ থেকে ডিকশনারি যে ক্রমে জিনিস ঢোকানো হয়েছে সেই ক্রমই মনে রাখে, আর ছাপার সময়ও সেই ক্রমে দেখায়। কিন্তু ডিকশনারি ব্যবহার করা হয় নাম ধরে খোঁজার জন্য, অবস্থান ধরে নয় —marks[0]লিখলে পাইথন0-কে একটা key ভেবে খুঁজবে, আর না পেয়েKeyError: 0দিয়ে থেমে যাবে।
নেই এমন key — KeyError আর .get()
marks = {"rafi": 72}
print(marks["dia"])KeyError: 'dia'KeyError মানে “এই নামে কোনো key নেই”। লিস্টে একই ভুল IndexError দিত — ভিন্ন নাম, একই ধরনের ভুল।
থেমে যাওয়ার বদলে একটা নিরাপদ উত্তর চাইলে .get():
marks = {"rafi": 72}
print(marks.get("dia"))
print(marks.get("dia", 0))
print(marks.get("rafi", 0))None
0
72দ্বিতীয় আর্গুমেন্টটা হলো “না পেলে এটা দিও”। না দিলে None আসে।
কোনটা কখন? key-টা থাকারই কথা হলে marks["rafi"] লিখুন — না থাকলে সেটা একটা বাগ, আর KeyError সেটাকে সাথে সাথে ধরিয়ে দেয়। key-টা না থাকা স্বাভাবিক হলে .get() লিখুন। সবখানে .get() লিখে বেড়ালে ভুল ডেটাও নীরবে পার হয়ে যাবে, আর সেটাই আপনি খুঁজতে চাইছিলেন।
যোগ, বদল, বাদ
marks = {"rafi": 72}
marks["dia"] = 88
marks["rafi"] = 75
print(marks)
del marks["rafi"]
print(marks){'rafi': 75, 'dia': 88}
{'dia': 88}একই লাইন দুটো কাজ করে: key-টা না থাকলে যোগ হয়, থাকলে ভ্যালু বদলে যায়। আলাদা কোনো “যোগ করো” মেথড নেই।
আর এখান থেকেই একটা গুরুত্বপূর্ণ নিয়ম আসে — একটা key একবারই থাকতে পারে:
marks = {"rafi": 72, "rafi": 90}
print(marks){'rafi': 90}কোনো এরর নেই। দ্বিতীয়টা প্রথমটাকে চুপচাপ মুছে দিয়েছে। ফাইল থেকে ডেটা পড়ার সময় এটা নিয়মিত ঘটে, আর রেকর্ড হারানোর একটা সাধারণ কারণ।
in key খোঁজে, ভ্যালু নয়
marks = {"rafi": 72, "ahmed": 45}
print("rafi" in marks)
print(72 in marks)True
False72 ভ্যালু হিসেবে আছে ঠিকই, কিন্তু in কেবল key-গুলোর মধ্যে খোঁজে। ভ্যালুর মধ্যে খুঁজতে হলে স্পষ্ট করে বলতে হবে: 72 in marks.values()।
ঘোরা — তিনভাবে
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)
print("---")
for name, mark in marks.items():
print(name, mark)
print("---")
print(list(marks.keys()))
print(list(marks.values()))rafi
ahmed
---
rafi 72
ahmed 45
---
['rafi', 'ahmed']
[72, 45]প্রথমটা খেয়াল করার মতো: for name in marks লিখলে key পাওয়া যায়, ভ্যালু নয়। প্রথমবার এটা সবাইকে অবাক করে।
দুটোই দরকার হলে .items(), যা প্রতিটি চক্রে একটা টাপল দেয় — আর name, mark সেটাকে আনপ্যাক করে নেয়। গত অধ্যায়ের আনপ্যাকিং এখানেই সবচেয়ে বেশি কাজে লাগে।
ভ্যালু নিয়ে হিসাব করতে হলে .values():
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(sum(marks.values()))
print(max(marks.values()))
print(round(sum(marks.values()) / len(marks), 2))207
90
69.0কোন জিনিস key হতে পারে
key হতে হলে জিনিসটাকে অপরিবর্তনীয় হতে হয়। স্ট্রিং, সংখ্যা, টাপল — চলে। লিস্ট চলে না:
d = {}
d[["a", "b"]] = 1TypeError: unhashable type: 'list'“Unhashable” শব্দটা ব্যাখ্যা করার মতো। ডিকশনারি এত দ্রুত খুঁজে পায় কারণ সে প্রতিটি key থেকে একটা সংখ্যা (hash) হিসাব করে রাখে আর সেই সংখ্যা ধরে সরাসরি জায়গাটায় পৌঁছে যায়। key-টা পরে বদলে গেলে সেই হিসাবটা ভুল হয়ে যেত, আর জিনিসটা হারিয়ে যেত। তাই পাইথন বদলানো যায় এমন কিছুকে key হতেই দেয় না।
টাপল বদলানো যায় না, তাই টাপল চমৎকার key:
grid = {}
grid[(0, 0)] = "start"
grid[(1, 2)] = "tree"
print(grid)
print(grid[(1, 2)]){(0, 0): 'start', (1, 2): 'tree'}
treeগত অধ্যায়ে টাপলের অপরিবর্তনীয়তাকে সুবিধা বলা হয়েছিল। এটা তার প্রথম ব্যবহারিক প্রমাণ।
সবচেয়ে কাজের প্যাটার্ন — গোনা
words = ["pen", "bag", "pen", "ink", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts){'pen': 3, 'bag': 1, 'ink': 1}লাইনটা ভেতর থেকে পড়ুন: counts.get(word, 0) বলছে “এই শব্দের এখনকার গণনা দাও, আগে না থাকলে শূন্য ধরো”, তারপর তাতে এক যোগ করে আবার রেখে দেওয়া হচ্ছে।
.get()-এর ডিফল্ট ভ্যালুটাই এখানে পুরো কাজ করছে — সেটা না থাকলে প্রতিটি নতুন শব্দে KeyError আসত, আর if word in counts: লিখে দুটো শাখা সামলাতে হতো। এই এক লাইনের প্যাটার্নটা আপনি বহুবার লিখবেন।
একটা সম্পূর্ণ উদাহরণ
marks.py:
# A dictionary keeps a name attached to its value
marks = {"rafi": 72, "ahmed": 45, "bilal": 90, "dia": 88}
print("Everyone:")
for name, mark in marks.items():
status = "pass" if mark >= 40 else "fail"
print(f" {name:<8} {mark:>3} {status}")
print()
print("Count :", len(marks))
print("Total :", sum(marks.values()))
print("Average :", round(sum(marks.values()) / len(marks), 2))
print("Highest :", max(marks.values()))
best = max(marks, key=marks.get)
print("Best :", best, marks[best])
print("Has dia :", "dia" in marks)
print("Missing :", marks.get("nadia", "not recorded"))
marks["dia"] = 91
marks["nadia"] = 60
del marks["ahmed"]
print()
print("After edits:", marks)Everyone:
rafi 72 pass
ahmed 45 pass
bilal 90 pass
dia 88 pass
Count : 4
Total : 295
Average : 73.75
Highest : 90
Best : bilal 90
Has dia : True
Missing : not recorded
After edits: {'rafi': 72, 'bilal': 90, 'dia': 91, 'nadia': 60}তিনটা জিনিস লক্ষ করার মতো।
status = "pass" if mark >= 40 else "fail" — এটা একটা শর্তকে এক লাইনে লেখার উপায়, যা পড়া হয় এভাবে: “mark >= 40 হলে "pass", নইলে "fail"”। ৯ নম্বর অধ্যায়ের চার লাইনের if/else এখানে একটিমাত্র ভ্যালু বেছে নিচ্ছে, তাই এক লাইনই যথেষ্ট। বড় সিদ্ধান্তের জন্য পূর্ণ if লিখুন — এটা কেবল “দুটোর একটা ভ্যালু” ধরনের কাজের জন্য।
max(marks, key=marks.get) — ১৩ নম্বর অধ্যায়ের key=, নতুন কাজে। (এখানে key= হলো max-এর আর্গুমেন্টের নাম, ডিকশনারির key নয়।) max(marks) key-গুলোর মধ্যে সবচেয়ে বড়টা দিত, অর্থাৎ বর্ণানুক্রমে শেষ নামটা। key=marks.get বলছে প্রতিটি key-কে তার ভ্যালু দিয়ে তুলনা করো — তাই সবচেয়ে বেশি নম্বরওয়ালা নামটা আসে। খেয়াল করুন সর্বোচ্চ নম্বরটা পাওয়া সহজ (max(marks.values())), কিন্তু সেটা কার তা জানতে এই কৌশলটা লাগে।
শেষ লাইনের ক্রম। dia আগে থেকেই ছিল, তাই তার ভ্যালু বদলেছে কিন্তু জায়গা বদলায়নি। nadia নতুন, তাই শেষে যোগ হয়েছে। আর ahmed মুছে গেছে। ডিকশনারি ঢোকানোর ক্রম মনে রাখে — একটা ভ্যালু বদলালে সেই ক্রম নড়ে না।
কিছু ভাঙা অবস্থা ও তার সমাধান
KeyError: 'dia' এই নামে কোনো key নেই। বানান আর অক্ষরের ছোট-বড় মিলিয়ে দেখুন — "Rafi" আর "rafi" দুটো আলাদা key। ফাইল থেকে পড়া key-তে প্রায়ই শেষে একটা স্পেস লেগে থাকে; print(list(d.keys())) দিয়ে দেখলে কোটেশনের ভেতরে সেটা ধরা পড়ে। key-টা না থাকা স্বাভাবিক হলে .get() ব্যবহার করুন।
TypeError: unhashable type: 'list' একটা লিস্টকে key বানানোর চেষ্টা। টাপল ব্যবহার করুন — tuple(my_list) সেটা বানিয়ে দেয়।
for x in my_dict লিখলে ভ্যালু পাচ্ছি না পাওয়ার কথাও নয় — ওটা key দেয়। ভ্যালুর জন্য .values(), দুটোর জন্য .items()।
দুটো key-ই একই, একটা রেকর্ড হারিয়ে গেছে একটা key একবারই থাকে, আর পরেরটা আগেরটাকে নীরবে বদলে দেয়। একই নামে একাধিক রেকর্ড রাখতে হলে ভ্যালু হিসেবে একটা লিস্ট রাখুন — ১৭ নম্বর অধ্যায়ের বিষয়।
KeyError: 0 my_dict[0] লেখা হয়েছে, লিস্টের মতো প্রথম আইটেমটা চেয়ে। ডিকশনারিতে অবস্থান বলে কিছু নেই; 0-কে একটা key হিসেবেই খোঁজা হয়, আর এমন key না থাকলে KeyError: 0 আসে। প্রথম জোড়াটা সত্যিই দরকার হলে list(my_dict.items())[0] লিখুন।
ডিকশনারির উপর .sort() চলে না ঠিক তাই — ডিকশনারির .sort() নেই। sorted(d.items()) একটা সাজানো লিস্ট দেয়, আর সেটাই সাধারণত যা দরকার।
ধাপ ৪ / ৬ — অনুমান
যাচাই করুন
একটি ডিকশনারির উপর সরাসরি লুপ চালানো হয়েছে। কী ছাপা হবে?
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)- Arafi 72 ahmed 45
- Brafi ahmed
- C72 45
- D('rafi', 72) ('ahmed', 45)
নেই এমন একটি key চাওয়া হয়েছে। কী হবে, আর নিরাপদ বিকল্প কী?
marks = {"rafi": 72}
print(marks["dia"])- A`None` ছাপা হয়
- Bখালি লাইন ছাপা হয়
- C`KeyError` — নিরাপদ বিকল্প `marks.get("dia")`, যা `None` দেয়
- D`IndexError`
গোনার প্যাটার্ন। কী ছাপা হবে?
words = ["pen", "bag", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)- Aএকটি `KeyError`
- B{'pen': 2, 'bag': 1}
- C{'pen': 1, 'bag': 1}
- D{'pen': 3, 'bag': 1}
উত্তর দিতে অ্যাকাউন্ট লাগবে
উত্তর মিলিয়ে দেখতে সাইন ইন করুন
প্রশ্নগুলো উপরে আছে, আর মাথায় মাথায় উত্তর ভেবে নেওয়াই আসল কাজ। সঠিক উত্তর, ব্যাখ্যা আর তিন ধাপের ইঙ্গিত দেখতে সাইন ইন করুন।
নিজে করুন
stock.py নামে একটা ফাইল লিখুন, যেখানে অন্তত ছয়টা পণ্যের একটা ডিকশনারি থাকবে — পণ্যের নাম key, আর দাম ভ্যালু।
তারপর:
- প্রতিটি পণ্য আর তার দাম সাজানো কলামে ছাপান
- মোট, গড়, সর্বোচ্চ আর সর্বনিম্ন দাম ছাপান
- সবচেয়ে দামি পণ্যটার নাম বের করুন — শুধু দাম নয়
- একটা নতুন পণ্য যোগ করুন, একটার দাম বদলান, আর একটা বাদ দিন; তারপর আবার পুরোটা ছাপান
.get()দিয়ে এমন একটা পণ্যের দাম চান যা নেই, আর ডিফল্ট হিসেবে একটা বার্তা দিন
তারপর একটা গোনার প্রোগ্রাম লিখুন: একটা লিস্টে কয়েকটা শব্দ রাখুন (কিছু একাধিকবার), আর counts.get(word, 0) + 1 প্যাটার্নটা দিয়ে প্রতিটি কতবার আছে গুনে ছাপান।
শেষে ইচ্ছে করে ভাঙুন: নেই এমন একটা key স্কয়ার ব্র্যাকেট দিয়ে চান, আর একটা লিস্টকে key বানানোর চেষ্টা করুন। দুটো আলাদা এরর আসবে — কোনটা কেন, নিজেকে বুঝিয়ে বলুন।
ধাপ ৬ / ৬
কঠিন করা — অধ্যায়ের কুইজ
সহজ থেকে কঠিন — দশটি প্রশ্ন, শেষেরগুলো ইচ্ছে করেই কঠিন।
সাইন ইন করে কুইজ দিন