- Exception কী? — Error ও Exception-এর পার্থক্য
- Python-এর সাধারণ Exception-গুলো
- try...except — Exception ধরা
- একাধিক except Block
- else Block — কোনো Error না হলে
- finally Block — সবসময় চলে
- raise — নিজে Exception তোলা
- Custom Exception তৈরি করা
- Exception Chaining ও re-raise
- Context Manager ও with Statement
- প্র্যাকটিক্যাল প্রজেক্ট — Robust Calculator
- সারাংশ ও অনুশীলন
বাস্তব জীবনে প্রোগ্রাম চলার সময় অনেক ধরনের সমস্যা হতে পারে — ব্যবহারকারী ভুল ডেটা দিল, File পাওয়া গেল না, নেটওয়ার্ক বিচ্ছিন্ন হলো। এই সমস্যাগুলো সামলাতে না পারলে প্রোগ্রাম crash করে। Python-এ এই সমস্যাগুলো সুন্দরভাবে সামলানোর পদ্ধতিকে বলে Exception Handling।
Python-এ দুই ধরনের সমস্যা আছে:
| ধরন | কী | উদাহরণ | সামলানো যায়? |
|---|---|---|---|
| Syntax Error | কোড লেখায় ভুল | কোলন না দেওয়া, Indentation ভুল | ❌ না — ঠিক করতে হবে |
| Exception | Runtime-এ সমস্যা | শূন্য দিয়ে ভাগ, File না পাওয়া | ✅ হ্যাঁ — try-except দিয়ে |
Exception Handle না করলে কী হয়:
# শূন্য দিয়ে ভাগ result = 10 / 0 print(result) # এই লাইনে কখনো পৌঁছাবে না!
Traceback (most recent call last):
File "main.py", line 2, in <module>
result = 10 / 0
ZeroDivisionError: division by zero
Traceback হলো Python-এর Error বার্তা — কোন File-এর কত নম্বর লাইনে কোন Exception হয়েছে সেটা বলে। Exception Handle করলে এই crash এড়ানো যায়।
শূন্য দিয়ে ভাগ করলে
ভুল ধরনের মান দিলে (যেমন int("abc"))
ভুল ধরনের অপারেশন ("a" + 1)
List-এর বাইরের Index
Dict-এ Key না থাকলে
File পাওয়া না গেলে
অপরিচিত Variable নাম
Object-এ Method/Attribute নেই
Module import করতে না পারলে
Iterator শেষ হলে
সংখ্যা অনেক বড় হলে
Memory শেষ হলে
Python-এর সব Exception একটি Tree Structure-এ সাজানো। উপরের Exception ধরলে নিচের সব ধরা পড়ে।
try Block-এ সেই কোড রাখো যেখানে Error হতে পারে। Error হলে except Block চলে।
# Exception ছাড়া — crash! # num = int(input("সংখ্যা দাও: ")) # "abc" দিলে crash # Exception দিয়ে — নিরাপদ try: num = int(input("একটি সংখ্যা দাও: ")) result = 100 / num print(f"100 ÷ {num} = {result}") except ValueError: print("❌ ভুল! সংখ্যা দিতে হবে।") except ZeroDivisionError: print("❌ শূন্য দিয়ে ভাগ করা যায় না!") print("প্রোগ্রাম চলছে...") # এটি সবসময় চলবে
একটি সংখ্যা দাও: abc ❌ ভুল! সংখ্যা দিতে হবে। প্রোগ্রাম চলছে...
একটি সংখ্যা দাও: 0 ❌ শূন্য দিয়ে ভাগ করা যায় না! প্রোগ্রাম চলছে...
একটি সংখ্যা দাও: 5 100 ÷ 5 = 20.0 প্রোগ্রাম চলছে...
একটি try Block-এ বিভিন্ন ধরনের Exception ধরার জন্য একাধিক except Block রাখা যায়।
def get_item(lst, index): try: result = lst[index] print(f"✅ পাওয়া গেছে: {result}") except IndexError: print(f"❌ IndexError: index {index} নেই (List-এ {len(lst)}টি উপাদান)") except TypeError: print("❌ TypeError: index সংখ্যা হতে হবে") fruits = ["আম", "কলা", "লিচু"] get_item(fruits, 1) # ঠিক আছে get_item(fruits, 10) # IndexError get_item(fruits, "a") # TypeError # একসাথে একাধিক Exception ধরা try: x = int("abc") except (ValueError, TypeError): print("❌ ValueError বা TypeError হয়েছে") # সব Exception একসাথে (সাধারণ Exception) try: y = 10 / 0 except Exception as e: print(f"❌ কোনো Error হয়েছে: {e}") print(f" ধরন: {type(e).__name__}")
✅ পাওয়া গেছে: কলা ❌ IndexError: index 10 নেই (List-এ 3টি উপাদান) ❌ TypeError: index সংখ্যা হতে হবে ❌ ValueError বা TypeError হয়েছে ❌ কোনো Error হয়েছে: division by zero ধরন: ZeroDivisionError
except Exception দিয়ে সব Exception ধরা যায়, কিন্তু এটি সবসময় ভালো না। Specific Exception ধরলে কোড পড়তে সহজ হয় এবং Debugging সহজ হয়।
else Block চলে শুধুমাত্র যখন try Block-এ কোনো Exception হয়নি। এটি "সফলভাবে কাজ হলে" এই অংশের কোড রাখার জন্য।
def divide(a, b): try: result = a / b except ZeroDivisionError: print("❌ শূন্য দিয়ে ভাগ করা যায় না!") except TypeError: print("❌ সংখ্যা দিতে হবে!") else: # শুধুমাত্র Error না হলে চলবে print(f"✅ {a} ÷ {b} = {result:.2f}") return result divide(10, 4) # সফল divide(10, 0) # ZeroDivisionError divide(10, "a") # TypeError # ফাইল পড়ার উদাহরণ try: with open("students.txt", "r", encoding="utf-8") as f: data = f.read() except FileNotFoundError: print("❌ File পাওয়া যায়নি!") else: print(f"✅ File পড়া হয়েছে ({len(data)} অক্ষর)")
✅ 10 ÷ 4 = 2.50 ❌ শূন্য দিয়ে ভাগ করা যায় না! ❌ সংখ্যা দিতে হবে! ✅ File পড়া হয়েছে (52 অক্ষর)
finally Block সবসময় চলে — Error হোক বা না হোক, এমনকি return করলেও। Database Connection বা File বন্ধ করার কাজ এখানে করা হয়।
def process_file(filename): f = None try: print(f"📂 '{filename}' খোলা হচ্ছে...") f = open(filename, "r", encoding="utf-8") content = f.read() print(f"✅ পড়া হয়েছে।") return content except FileNotFoundError: print(f"❌ '{filename}' পাওয়া যায়নি!") return None finally: # Error হোক বা না হোক, return হোক বা না হোক — সবসময় চলে if f: f.close() print("🔒 File বন্ধ হয়েছে।") print("🏁 কাজ শেষ।\n") process_file("students.txt") # সফল process_file("missing.txt") # Error
📂 'students.txt' খোলা হচ্ছে... ✅ পড়া হয়েছে। 🔒 File বন্ধ হয়েছে। 🏁 কাজ শেষ। 📂 'missing.txt' খোলা হচ্ছে... ❌ 'missing.txt' পাওয়া যায়নি! 🏁 কাজ শেষ।
বাস্তব জীবনে with statement ব্যবহার করলে finally লিখতে হয় না — Python স্বয়ংক্রিয়ভাবে বন্ধ করে।
কখনো কখনো আমাদের নিজেদের Exception তুলতে হয় — যেমন ব্যবহারকারীর Input বৈধ না হলে। raise দিয়ে এটি করা যায়।
def set_age(age): """বয়স সেট করে — বৈধতা যাচাই করে।""" if not isinstance(age, int): raise TypeError("বয়স অবশ্যই integer হতে হবে।") if age < 0: raise ValueError(f"বয়স ঋণাত্মক হতে পারে না: {age}") if age > 150: raise ValueError(f"অবাস্তব বয়স: {age}") print(f"✅ বয়স সেট হয়েছে: {age}") # বিভিন্ন Input পরীক্ষা for test in [25, -5, 200, "বিশ"]: try: set_age(test) except (ValueError, TypeError) as e: print(f"❌ {type(e).__name__}: {e}")
✅ বয়স সেট হয়েছে: 25 ❌ ValueError: বয়স ঋণাত্মক হতে পারে না: -5 ❌ ValueError: অবাস্তব বয়স: 200 ❌ TypeError: বয়স অবশ্যই integer হতে হবে।
নিজের প্রজেক্টের জন্য কাস্টম Exception তৈরি করা যায়। এতে Error বার্তা আরও স্পষ্ট হয়।
# Custom Exception — Exception Class থেকে Inherit করে class InsufficientBalanceError(Exception): """ব্যালেন্স কম হলে এই Exception।""" def __init__(self, balance, amount): self.balance = balance self.amount = amount message = f"যথেষ্ট ব্যালেন্স নেই! বর্তমান: {balance} টাকা, দরকার: {amount} টাকা" super().__init__(message) class InvalidAmountError(Exception): """পরিমাণ অবৈধ হলে।""" pass class BankAccount: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance def withdraw(self, amount): if amount <= 0: raise InvalidAmountError("পরিমাণ অবশ্যই শূন্যের বেশি হতে হবে।") if amount > self.balance: raise InsufficientBalanceError(self.balance, amount) self.balance -= amount print(f"✅ {amount} টাকা উত্তোলন। বাকি: {self.balance}") # পরীক্ষা acc = BankAccount("রাহিম", 5000) for amount in [2000, -100, 10000]: try: acc.withdraw(amount) except InvalidAmountError as e: print(f"❌ InvalidAmountError: {e}") except InsufficientBalanceError as e: print(f"❌ InsufficientBalanceError: {e}")
✅ 2000 টাকা উত্তোলন। বাকি: 3000 ❌ InvalidAmountError: পরিমাণ অবশ্যই শূন্যের বেশি হতে হবে। ❌ InsufficientBalanceError: যথেষ্ট ব্যালেন্স নেই! বর্তমান: 3000 টাকা, দরকার: 10000 টাকা
# re-raise — ধরার পর আবার তোলা def risky_operation(): try: result = 10 / 0 except ZeroDivisionError: print("Log করলাম: ZeroDivisionError হয়েছে") raise # আবার তোলো try: risky_operation() except ZeroDivisionError as e: print(f"বাইরে ধরা হলো: {e}") print() # Exception Chaining — raise X from Y def load_config(filename): try: with open(filename) as f: return f.read() except FileNotFoundError as e: raise RuntimeError(f"Config load করতে পারিনি: {filename}") from e try: load_config("config.json") except RuntimeError as e: print(f"❌ RuntimeError: {e}") print(f" Caused by: {e.__cause__}")
Log করলাম: ZeroDivisionError হয়েছে বাইরে ধরা হলো: division by zero ❌ RuntimeError: Config load করতে পারিনি: config.json Caused by: [Errno 2] No such file or directory: 'config.json'
# Pattern ১: সঠিক Input না আসা পর্যন্ত চাও def get_valid_number(prompt, min_val=None, max_val=None): while True: try: num = float(input(prompt)) if min_val is not None and num < min_val: print(f" ন্যূনতম {min_val} হতে হবে।") continue if max_val is not None and num > max_val: print(f" সর্বোচ্চ {max_val} হতে পারবে।") continue return num except ValueError: print(" সংখ্যা দিতে হবে! আবার চেষ্টা করো।") # Pattern ২: Default মান দিয়ে চালিয়ে যাওয়া def safe_int(value, default=0): try: return int(value) except (ValueError, TypeError): return default print(safe_int("42")) # 42 print(safe_int("abc")) # 0 (default) print(safe_int("abc", -1)) # -1 (custom default) print(safe_int(None)) # 0
42 0 -1 0
Exception Handling-এর সব ধারণা ব্যবহার করে একটি Crash-proof Calculator তৈরি করি।
যেকোনো ভুল Input-এও crash করবে না। Custom Exception, সব ধরনের Error সামলানো এবং History সংরক্ষণ সহ।
# Custom Exception class CalculatorError(Exception): pass class DivisionByZeroError(CalculatorError): pass class InvalidOperatorError(CalculatorError): pass history = [] def calculate(a, op, b): """হিসাব করে — সব ধরনের Error সামলায়।""" valid_ops = ["+", "-", "*", "/", "//", "%", "**"] if op not in valid_ops: raise InvalidOperatorError(f"অজানা অপারেটর: '{op}' | বৈধ: {valid_ops}") if op in ["/", "//", "%"] and b == 0: raise DivisionByZeroError("শূন্য দিয়ে ভাগ করা যায় না!") ops = { "+": lambda x,y: x+y, "-": lambda x,y: x-y, "*": lambda x,y: x*y, "/": lambda x,y: x/y, "//": lambda x,y: x//y, "%": lambda x,y: x%y, "**": lambda x,y: x**y, } return ops[op](a, b) def get_number(prompt): """বৈধ সংখ্যা না পাওয়া পর্যন্ত চাও।""" while True: try: return float(input(prompt)) except ValueError: print(" ⚠️ সংখ্যা দিতে হবে!") def run_calculator(): print("╔══════════════════════════╗") print("║ Robust Calculator 🧮 ║") print("╚══════════════════════════╝") print("অপারেটর: + - * / // % ** | 'q' = বের হও\n") while True: try: a = get_number("প্রথম সংখ্যা: ") op = input("অপারেটর: ").strip() if op.lower() == "q": print("👋 বিদায়!") break b = get_number("দ্বিতীয় সংখ্যা: ") result = calculate(a, op, b) expr = f"{a} {op} {b} = {result}" history.append(expr) print(f" ✅ {expr}\n") except DivisionByZeroError as e: print(f" ❌ {e}\n") except InvalidOperatorError as e: print(f" ❌ {e}\n") except OverflowError: print(" ❌ ফলাফল অনেক বড়!\n") except KeyboardInterrupt: print("\n👋 Ctrl+C চেপে বের হলে।") break finally: if history: print(f" 📋 History ({len(history)} টি): {history[-1]}") # চালাও run_calculator()
╔══════════════════════════╗ ║ Robust Calculator 🧮 ║ ╚══════════════════════════╝ অপারেটর: + - * / // % ** | 'q' = বের হও প্রথম সংখ্যা: 10 অপারেটর: / দ্বিতীয় সংখ্যা: 3 ✅ 10.0 / 3.0 = 3.3333333333333335 📋 History (1 টি): 10.0 / 3.0 = 3.3333333333333335 প্রথম সংখ্যা: 5 অপারেটর: / দ্বিতীয় সংখ্যা: 0 ❌ শূন্য দিয়ে ভাগ করা যায় না! 📋 History (1 টি): 10.0 / 3.0 = 3.3333333333333335 প্রথম সংখ্যা: 2 অপারেটর: ** দ্বিতীয় সংখ্যা: 10 ✅ 2.0 ** 10.0 = 1024.0 📋 History (2 টি): 2.0 ** 10.0 = 1024.0
- Exception কী — Syntax Error ও Exception-এর পার্থক্য
- Python-এর সাধারণ Exception ও Exception Hierarchy
- try...except — Exception ধরা ও সামলানো
- একাধিক except Block ও একসাথে একাধিক Exception
- except Exception as e — Error বার্তা ও ধরন জানা
- else Block — Error না হলে চলে
- finally Block — সবসময় চলে, Resource পরিষ্কার করতে
- raise — নিজে Exception তোলা
- Custom Exception — নিজের Exception Class তৈরি
- re-raise ও Exception Chaining (raise X from Y)
- সাধারণ Patterns — while loop দিয়ে valid input, safe conversion
- Robust Calculator প্রজেক্ট
- Safe Input: একটি Function বানাও যা বয়স input নেয় — text দিলে, ঋণাত্মক দিলে, ১৫০-এর বেশি দিলে সঠিক Error message দিয়ে আবার চাইবে
- Safe File Reader: একটি Function বানাও যা file path নিয়ে পড়ে — না পেলে, Permission না থাকলে আলাদা message দেয়
- Custom Exception: একটি Shopping Cart-এর জন্য OutOfStockError ও InvalidQuantityError Custom Exception তৈরি করো
- Finally Demonstration: Database connection simulate করো — যেকোনো Error হলেও connection "বন্ধ" হয় সেটা finally দিয়ে দেখাও
- JSON Parser: একটি JSON-like string parse করো — ভুল format হলে Custom Exception দিয়ে সুন্দর Error message দেখাও
- Module কী — import, from...import, as alias
- Python Standard Library-র গুরুত্বপূর্ণ Modules
- নিজের Module তৈরি করা
- Package কী এবং __init__.py
- pip দিয়ে Third-party Package ইনস্টল করা

0 Comments