Lesson ১১৭ মিনিট পড়া

এরর হ্যান্ডলিং (Error Handling)

যখন কোনো কিছু ভুল হয় — এবং তা হবেই — তখন তার জন্য প্রস্তুত থাকুন

কেন এরর হয় (এবং এটি স্বাভাবিক) (Why Errors Happen - And That's OK)

প্রোগ্রামিংয়ে এরর বা ভুল হওয়াটা খুবই সাধারণ একটি ব্যাপার — ঠিক যেমন রাস্তায় গর্ত থাকা। মূল প্রশ্নটি এটা নয় যে "আমি কি গর্তে পড়ব?" বরং প্রশ্ন হলো "গর্তে পড়লে আমি কি সেটার জন্য প্রস্তুত?" পাইথনের এরর হ্যান্ডলিং আপনার প্রোগ্রামটিকে ক্র্যাশ করে বন্ধ হয়ে যাওয়ার বদলে, ভুলগুলোকে সুন্দরভাবে সামলানোর সুযোগ দেয়।

পাইথনে দুই ধরনের সমস্যা দেখা দেয়:

  • সিনট্যাক্স এরর (Syntax errors) — আপনি এমন কিছু টাইপ করেছেন যা পাইথন বুঝতে পারছে না। অনেকটা রেসিপিতে ভুল বানানের মতো: prnt("hello")। পাইথন এগুলো কোড রান করার আগেই ধরে ফেলে।
  • এক্সেপশন (Exceptions) — আপনার লেখা কোডটি পাইথন বুঝতে পারে, কিন্তু রান করার সময় (runtime) কোনো সমস্যা হয়েছে: যেমন শূন্য দিয়ে ভাগ করা, এমন কোনো ফাইল খোলার চেষ্টা করা যার কোনো অস্তিত্ব নেই, বা লিস্টের এমন কোনো ইনডেক্সে প্রবেশ করার চেষ্টা করা যা লিস্টের সীমানার বাইরে।

সাধারণ এক্সেপশনসমূহ (Common Exceptions)

# Let's see some common exceptions (don't worry, we'll catch them!)
examples = [
("ZeroDivisionError", lambda: 10 / 0),
("IndexError", lambda: [1, 2, 3][99]),
("KeyError", lambda: {"a": 1}["z"]),
("TypeError", lambda: "hello" + 5),
("ValueError", lambda: int("abc")),
("FileNotFoundError", lambda: open("nope.txt")),
]
for name, func in examples:
try:
func()
except Exception as e:
print(f"{name}: {e}")
Output
ZeroDivisionError: division by zero
IndexError: list index out of range
KeyError: 'z'
TypeError: can only concatenate str (not "int") to str
ValueError: invalid literal for int() with base 10: 'abc'
FileNotFoundError: [Errno 2] No such file or directory: 'nope.txt'

Try / Except / Else / Finally (Try / Except / Else / Finally)

try/except ব্লক হলো আপনার সেফটি নেট বা সুরক্ষাজাল। এর চারটি অংশ যেভাবে কাজ করে:

  • try — ঝুঁকিপূর্ণ কোডটি এখানে রাখুন। "এটি চেষ্টা করুন, এবং যদি এটি সমস্যা করে..."
  • except — "...সেই সমস্যাটি ধরুন এবং সামলান।" আপনি নির্দিষ্ট কোনো এক্সেপশন বা সবগুলো এক্সেপশনই ধরতে পারেন।
  • else — এটি কেবলমাত্র তখনই রান করে যখন try কোনো এরর ছাড়াই সফল হয়। "যদি সবকিছু ঠিকঠাক থাকে, তবে এটাও করুন।"
  • finally — এরর হোক বা না হোক, এটি যাই হোক না কেন রান করবেই। কিছু ক্লিনআপ বা পরিষ্কার করার কাজের জন্য এটি দারুণ। "কাজ শেষ হলে সবসময় এটি করুন।"
Click chart to zoom
try/except/else/finally — else কেবল সফল হলেই রান করে, finally যাই হোক না কেন রান করবেই

পূর্ণাঙ্গ Try/Except প্যাটার্ন (The Full Try/Except Pattern)

def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("Can't divide by zero!")
return None
except TypeError:
print("Both arguments must be numbers!")
return None
else:
print(f"{a} / {b} = {result}") # Only runs if no error
return result
finally:
print("--- Division attempt complete ---") # Always runs
safe_divide(10, 3)
print()
safe_divide(10, 0)
print()
safe_divide(10, "two")
Output
10 / 3 = 3.3333333333333335
--- Division attempt complete ---

Can't divide by zero!
--- Division attempt complete ---

Both arguments must be numbers!
--- Division attempt complete ---

এক্সেপশন রেইজ করা (Raising Exceptions)

মাঝেমধ্যে আপনি নিজে থেকেই ইচ্ছা করে কোনো এরর বা ত্রুটি ঘটাতে বা রেইজ (raise) করতে চাইতে পারেন। হয়তো কেউ আপনার ফাংশনে কোনো ভুল ডেটা পাস করেছে এবং আপনি একটি স্পষ্ট মেসেজ দিয়ে কাজটিকে শুরুতেই থামিয়ে দিতে চান। এর জন্য raise ব্যবহার করুন।

রেইজিং এবং কাস্টম এক্সেপশন (Raising & Custom Exceptions)

# Raising built-in exceptions
def set_age(age):
if not isinstance(age, int):
raise TypeError("Age must be an integer")
if age < 0 or age > 150:
raise ValueError(f"Age must be between 0 and 150, got {age}")
print(f"Age set to {age}")
set_age(25)
try:
set_age(-5)
except ValueError as e:
print(f"Caught: {e}")
print()
# Custom exception classes
class InsufficientFundsError(Exception):
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(
f"Can't withdraw ${amount}. Balance is only ${balance}."
)
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(balance, amount)
return balance - amount
try:
new_balance = withdraw(50, 75)
except InsufficientFundsError as e:
print(e)
print(f"You're short by ${e.amount - e.balance}")
Output
Age set to 25
Caught: Age must be between 0 and 150, got -5

Can't withdraw $75. Balance is only $50.
You're short by $25

বেস্ট প্র্যাক্টিসেস (Best Practices)

এরর হ্যান্ডলিংয়ের কিছু সুবর্ণ নিয়ম হলো:

  • নির্দিষ্ট করে উল্লেখ করুনexcept: লিখে সবকিছু না ধরে, নির্দিষ্টভাবে ValueError ধরুন। একটি ফাঁকা except সবকিছু ধরে ফেলে, যার মধ্যে কীবোর্ড ইন্টারাপ্ট বা সিস্টেম এক্সিটও থাকে। এটি প্রায় কখনোই আমাদের কাম্য নয়।
  • এররকে নীরব করে রাখবেন না — কখনোই except: pass লিখবেন না। যদি কোনো ভুল হয়, তবে আপনার সেটি জানা উচিত।
  • else ব্যবহার করুন — যেসব কোড কেবল সফল হলে চলা উচিত, সেগুলো try এর বদলে else এ রাখুন। এতে আপনি ভুল করে সফল হওয়া কোডের এরর ধরবেন না।
  • LBYL এর চেয়ে EAFP ভালো — পাইথনে একটি কথা খুব পরিচিত, "অনুমতি চাওয়ার চেয়ে ক্ষমা চাওয়া সহজ (Easier to Ask Forgiveness than Permission)"। প্রতিটি সম্ভাব্য শর্ত আগে থেকেই যাচাই (LBYL) করার চেয়ে, সরাসরি কাজটি করার চেষ্টা করুন এবং এরর হলে তা সামলে (catch) নিন।

LBYL এর চেয়ে EAFP ভালো (EAFP vs. LBYL)

data = {"name": "Anika", "scores": [85, 92, 78]}
# LBYL — Look Before You Leap (less Pythonic)
if "scores" in data and len(data["scores"]) > 0:
avg = sum(data["scores"]) / len(data["scores"])
print(f"LBYL average: {avg}")
# EAFP — Easier to Ask Forgiveness (more Pythonic)
try:
avg = sum(data["scores"]) / len(data["scores"])
print(f"EAFP average: {avg}")
except (KeyError, ZeroDivisionError) as e:
print(f"Couldn't calculate: {e}")
Output
LBYL average: 85.0
EAFP average: 85.0
Note: try/except-কে সিনেমার স্টান্ট ডাবলের (stunt double) মতো চিন্তা করুন। আপনি স্টান্ট ডাবলকে (try ব্লক) বিপজ্জনক কাজগুলো করতে দেন, আর যদি কোনো সমস্যা হয়, সেফটি ক্রু বা নিরাপত্তা দল (except ব্লক) তা সামলে নেয় — যাতে আপনার সিনেমা (আপনার প্রোগ্রাম) চলতে থাকে।
চ্যালেঞ্জ

ছোট কুইজ

try/except/else এর ক্ষেত্রে else ব্লকটি কখন রান করে?

পড়া চালিয়ে যান

File I/OClasses & OOP