Hot Post

6/recent/ticker-posts

Header Ads Widget

Python শেখার সিরিজ — পর্ব ১১ / ২০ (Exception Handling)

🐍 Python শেখার সিরিজ — পর্ব ১১ / ২০
python learning

Exception Handling
Error সামলানোর সঠিক উপায় — try, except, else, finally, raise, Custom Exception এবং Robust Calculator প্রজেক্ট
⏱ পড়তে সময় ~১৭ মিনিট 🎯 Beginner Friendly 🐍 Python 3.x

বাস্তব জীবনে প্রোগ্রাম চলার সময় অনেক ধরনের সমস্যা হতে পারে — ব্যবহারকারী ভুল ডেটা দিল, File পাওয়া গেল না, নেটওয়ার্ক বিচ্ছিন্ন হলো। এই সমস্যাগুলো সামলাতে না পারলে প্রোগ্রাম crash করে। Python-এ এই সমস্যাগুলো সুন্দরভাবে সামলানোর পদ্ধতিকে বলে Exception Handling

⚠️ Exception কী? — Error ও Exception-এর পার্থক্য

Python-এ দুই ধরনের সমস্যা আছে:

ধরনকীউদাহরণসামলানো যায়?
Syntax Errorকোড লেখায় ভুলকোলন না দেওয়া, Indentation ভুল❌ না — ঠিক করতে হবে
ExceptionRuntime-এ সমস্যাশূন্য দিয়ে ভাগ, File না পাওয়া✅ হ্যাঁ — try-except দিয়ে

Exception Handle না করলে কী হয়:

Python — Exception Handle না করলে
# শূন্য দিয়ে ভাগ
result = 10 / 0
print(result)    # এই লাইনে কখনো পৌঁছাবে না!
❌ Error (Program Crash)
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 এড়ানো যায়।

📋 Python-এর সাধারণ Exception-গুলো
ZeroDivisionError

শূন্য দিয়ে ভাগ করলে

ValueError

ভুল ধরনের মান দিলে (যেমন int("abc"))

TypeError

ভুল ধরনের অপারেশন ("a" + 1)

IndexError

List-এর বাইরের Index

KeyError

Dict-এ Key না থাকলে

FileNotFoundError

File পাওয়া না গেলে

NameError

অপরিচিত Variable নাম

AttributeError

Object-এ Method/Attribute নেই

ImportError

Module import করতে না পারলে

StopIteration

Iterator শেষ হলে

OverflowError

সংখ্যা অনেক বড় হলে

MemoryError

Memory শেষ হলে

Exception-এর Hierarchy

Python-এর সব Exception একটি Tree Structure-এ সাজানো। উপরের Exception ধরলে নিচের সব ধরা পড়ে।

BaseException ├── SystemExit ├── KeyboardInterrupt └── Exception ← এটি সবচেয়ে বেশি ব্যবহৃত ├── ValueError ├── TypeError ├── ArithmeticError │ └── ZeroDivisionError ├── LookupError │ ├── IndexError │ └── KeyError └── OSError └── FileNotFoundError
🛡️ try...except — Exception ধরা

try Block-এ সেই কোড রাখো যেখানে Error হতে পারে। Error হলে except Block চলে।

try:যে কোডে Error হতে পারে সেটা এখানে রাখো। Error হলে এখান থেকে বেরিয়ে except-এ যায়।
except ExceptionType:Error হলে এই Block চলে। Error সামলাও এখানে।
else:Error না হলে এই Block চলে। (ঐচ্ছিক)
finally:Error হোক বা না হোক, সবসময় চলে। (ঐচ্ছিক)
Python — try...except
# 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 দিলে)
একটি সংখ্যা দাও: abc
❌ ভুল! সংখ্যা দিতে হবে।
প্রোগ্রাম চলছে...
✔ আউটপুট (0 দিলে)
একটি সংখ্যা দাও: 0
❌ শূন্য দিয়ে ভাগ করা যায় না!
প্রোগ্রাম চলছে...
✔ আউটপুট (5 দিলে)
একটি সংখ্যা দাও: 5
100 ÷ 5 = 20.0
প্রোগ্রাম চলছে...
🔀 একাধিক except Block

একটি try Block-এ বিভিন্ন ধরনের Exception ধরার জন্য একাধিক except Block রাখা যায়।

Python — Multiple except
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 — কোনো Error না হলে

else Block চলে শুধুমাত্র যখন try Block-এ কোনো Exception হয়নি। এটি "সফলভাবে কাজ হলে" এই অংশের কোড রাখার জন্য।

Python — try...except...else
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 — সবসময় চলে

finally Block সবসময় চলে — Error হোক বা না হোক, এমনকি return করলেও। Database Connection বা File বন্ধ করার কাজ এখানে করা হয়।

Python — finally Block
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 স্বয়ংক্রিয়ভাবে বন্ধ করে।

🚀 raise — নিজে Exception তোলা

কখনো কখনো আমাদের নিজেদের Exception তুলতে হয় — যেমন ব্যবহারকারীর Input বৈধ না হলে। raise দিয়ে এটি করা যায়।

Python — raise Exception
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 হতে হবে।
🏗️ Custom Exception তৈরি করা

নিজের প্রজেক্টের জন্য কাস্টম Exception তৈরি করা যায়। এতে Error বার্তা আরও স্পষ্ট হয়।

Python — Custom Exception
# 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 টাকা
🔗 Exception Chaining ও re-raise
Python — re-raise ও Exception Chaining
# 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'
🔁 সাধারণ Exception Patterns
Python — সাধারণ Exception Patterns
# 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
🧮 প্র্যাকটিক্যাল প্রজেক্ট — Robust Calculator

Exception Handling-এর সব ধারণা ব্যবহার করে একটি Crash-proof Calculator তৈরি করি।

💻 প্রজেক্ট: Robust Calculator

যেকোনো ভুল Input-এও crash করবে না। Custom Exception, সব ধরনের Error সামলানো এবং History সংরক্ষণ সহ।

Python — robust_calculator.py
# 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 প্রজেক্ট
🏋️ নিজে চেষ্টা করো
  1. Safe Input: একটি Function বানাও যা বয়স input নেয় — text দিলে, ঋণাত্মক দিলে, ১৫০-এর বেশি দিলে সঠিক Error message দিয়ে আবার চাইবে
  2. Safe File Reader: একটি Function বানাও যা file path নিয়ে পড়ে — না পেলে, Permission না থাকলে আলাদা message দেয়
  3. Custom Exception: একটি Shopping Cart-এর জন্য OutOfStockErrorInvalidQuantityError Custom Exception তৈরি করো
  4. Finally Demonstration: Database connection simulate করো — যেকোনো Error হলেও connection "বন্ধ" হয় সেটা finally দিয়ে দেখাও
  5. JSON Parser: একটি JSON-like string parse করো — ভুল format হলে Custom Exception দিয়ে সুন্দর Error message দেখাও
⏭️ পরের পর্বে কী থাকছে? — পর্ব ১২: Modules ও Packages
  • Module কী — import, from...import, as alias
  • Python Standard Library-র গুরুত্বপূর্ণ Modules
  • নিজের Module তৈরি করা
  • Package কী এবং __init__.py
  • pip দিয়ে Third-party Package ইনস্টল করা

Post a Comment

0 Comments