मूल बातें

Python में एक्सेप्शन: try, except, else, finally, raise और अपने एक्सेप्शन

अपडेट किया गया: · Skillaria.Top

Python में एक्सेप्शन वह ऑब्जेक्ट है जो एरर का संकेत देता है और सामान्य प्रवाह रोक देता है, जब तक उसे try/except ब्लॉक न पकड़ ले; else तब चलता है जब कोई एरर न हो, और finally हमेशा चलता है। एक्सेप्शन खुद उठाने के लिए raise स्टेटमेंट है, जैसे raise ValueError("राशि ऋणात्मक है"), और अपना एक्सेप्शन Exception के उत्तराधिकारी क्लास से बनता है।

Python में एक्सेप्शन एक ऐसा ऑब्जेक्ट है जो एरर का संकेत देता है और कोड का सामान्य प्रवाह रोक देता है। अगर उसे किसी ने न पकड़ा हो, तो Python traceback छापता है और प्रोग्राम बंद हो जाता है। एक्सेप्शन पकड़ने के लिए try/except है, और खुद उठाने के लिए raise। यहाँ देखते हैं कि यह कैसे काम करता है, कौन-सी एरर सबसे आम हैं, और एरर संभालने को एरर छिपाने में कैसे न बदलें।

Python में एक्सेप्शन क्या है

जब कोई ऑपरेशन पूरा न हो सके — शून्य से भाग, मौजूद न होने वाली कुंजी खोजना, शब्द को संख्या में बदलना — Python एक एक्सेप्शन ऑब्जेक्ट बनाता है और उसे «उठाता» (raise) है। मौजूदा फ़ंक्शन का काम रुक जाता है, और एक्सेप्शन कॉल की श्रृंखला में ऊपर जाता है, जब तक कोई उपयुक्त हैंडलर न मिले। न मिले तो प्रोग्राम गिर जाता है।

>>> 10 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

एक आम सवाल का जवाब: Python में शून्य से भाग देने पर ZeroDivisionError उठता है। यह /, // और %, तीनों पर लागू होता है।

traceback कैसे पढ़ें

Traceback एक रिपोर्ट है कि प्रोग्राम एरर तक कैसे पहुँचा। उसे नीचे से ऊपर पढ़ना आसान है। आख़िरी पंक्ति सबसे अहम है: एक्सेप्शन का प्रकार और संदेश। उसके ऊपर वह कोड-पंक्ति है जहाँ गड़बड़ी हुई, और उसके ऊपर वह जगह जहाँ से उसे बुलाया गया, और इसी तरह प्रवेश-बिंदु तक।

Traceback (most recent call last):
  File "tracker.py", line 9, in <module>
    total = parse_amount(row)
  File "tracker.py", line 4, in parse_amount
    return float(raw.strip())
           ~~~~~^^^^^^^^^^^^^
ValueError: could not convert string to float: 'सौ'

Python 3.11 से कोड की लाइन के नीचे ~~~^^^ जैसे निशान बने हैं (PEP 657): वे बताते हैं कि लाइन में कौन-सा ठीक-ठीक व्यंजन टूटा। यह तब बहुत मदद करता है, जब एक ही पंक्ति में कई कॉल एक के बाद एक हों।

«Did you mean» के सुझाव

Python 3.10 से नाम में टाइपो होने पर एरर एक नज़दीकी विकल्प सुझाता है। Python 3.15 में ये सुझाव और समझदार हो गए हैं: दूसरी भाषाओं की आदतें भी पहचानते हैं। list.push लिखने पर append सुझाया जाएगा, और "कॉफ़ी".toUpperCase() पर मेथड upper। सुझाव भीतरी फ़ील्ड भी देखते हैं: अगर डिक्शनरी ऑब्जेक्ट के totals एट्रिब्यूट में है और आपने सीधे ऑब्जेक्ट पर items() बुलाया, तो .totals.items सुझाया जाएगा। delattr() भी नज़दीकी नाम सुझाता है।

Python एक्सेप्शन के प्रकार

बिल्ट-इन एक्सेप्शन दर्जनों हैं, पर शुरुआत में यही सबसे ज़्यादा दिखते हैं:

एक्सेप्शनकब उठता हैउदाहरण
NameErrorनाम परिभाषित नहीं, अक्सर टाइपोprint(totl)
TypeErrorयह ऑपरेशन इस प्रकार के लिए सही नहीं"5" + 3
ValueErrorप्रकार सही, मान नहींint("सौ")
KeyErrorडिक्शनरी में ऐसी कुंजी नहींd["missing"]
IndexErrorइंडेक्स सूची की सीमा से बाहर[1, 2][5]
AttributeErrorऑब्जेक्ट में ऐसा मेथड या फ़ील्ड नहीं[].push(1)
ZeroDivisionErrorशून्य से भाग1 / 0
FileNotFoundErrorफ़ाइल नहीं मिलीopen("nope.txt")

अलग से SyntaxError (और उसका ख़ास रूप IndentationError) देखें: यह फ़ाइल पढ़ते समय, कोड चलने से पहले उठता है, इसलिए उसी फ़ाइल में try से पकड़ा नहीं जा सकता।

एक्सेप्शन का पदानुक्रम

एक्सेप्शन क्लास हैं, और वे एक पदानुक्रम (हायरार्की) में सजे हैं। शीर्ष पर BaseException है। उससे Exception (लगभग सारी «आम» एरर) और कुछ सिस्टम-स्तर वाले उत्तराधिकारी लेते हैं: KeyboardInterrupt, SystemExit, GeneratorExit।

BaseException
 ├── KeyboardInterrupt
 ├── SystemExit
 └── Exception
      ├── ArithmeticError
      │    └── ZeroDivisionError
      ├── LookupError
      │    ├── KeyError
      │    └── IndexError
      ├── OSError
      │    └── FileNotFoundError
      ├── TypeError
      └── ValueError

पदानुक्रम पकड़ते समय मायने रखता है: except LookupError KeyError और IndexError, दोनों पकड़ेगा। और KeyboardInterrupt कोड की गलती नहीं है: इससे Python बताता है कि उपयोगकर्ता ने Ctrl+C दबाया। वह जानबूझकर Exception से नहीं आता, ताकि except Exception प्रोग्राम को रोकने में बाधा न बने।

एक्सेप्शन संभालना: try/except/else/finally

जो कोड गिर सकता है, उसे try ब्लॉक में रखा जाता है। except में एरर का प्रकार लिखा जाता है और बताया जाता है कि क्या करना है। यही वह ब्लॉक है जिससे Python में एक्सेप्शन संभाले जाते हैं।

def parse_amount(raw):
    try:
        value = float(raw)
    except ValueError as e:
        print(f"पंक्ति छोड़ी जा रही है: {e}")
        return None
    else:
        return value          # तब चलता है, जब कोई एरर न हो
    finally:
        print("पार्सिंग पूरी हुई")   # हमेशा चलता है

print(parse_amount("250"))

यहाँ छपाई का क्रम है: पहले «पार्सिंग पूरी हुई», फिर 250.0। else ब्लॉक में return मान याद रखता है, फिर Python finally ज़रूर चलाता है और उसके बाद ही परिणाम लौटाता है। इसलिए finally सफ़ाई के लिए भरोसेमंद है: फ़ाइल बंद करना, कनेक्शन छोड़ना।

अगर finally में अपना return लिखा, तो वह पहले वाले को ढक देगा और एक्सेप्शन तक निगल सकता है। Python 3.14 से ऐसे कोड पर SyntaxWarning आता है (PEP 765) — finally में return न लिखें।

कई एक्सेप्शन एक साथ

अलग-अलग प्रकारों के लिए कई except ब्लॉक लिखे जा सकते हैं, या प्रकारों को टपल में दिया जा सकता है:

try:
    amount = int(data["amount"])
except KeyError:
    amount = 0
except (ValueError, TypeError) as e:
    print("ख़राब रकम:", e)
    amount = 0

ब्लॉक ऊपर से नीचे जाँचे जाते हैं, और पहला मेल खाने वाला चलता है, इसलिए ज़्यादा ख़ास प्रकार ऊपर रखें। Python 3.14 (PEP 758) से कई प्रकार बिना कोष्ठक के भी लिखे जा सकते हैं — except ValueError, TypeError: — पर अगर as हो, तो कोष्ठक अब भी ज़रूरी हैं।

एरर का संदेश और प्रकार कैसे देखें

एक्सेप्शन ऑब्जेक्ट as e से मिलता है। str(e) संदेश देता है, और type(e).__name__ प्रकार का नाम:

try:
    int("सौ")
except ValueError as e:
    print(type(e).__name__)   # ValueError
    print(e)                  # invalid literal for int() with base 10: 'सौ'

traceback मॉड्यूल: पूरा एरर-टेक्स्ट

कभी-कभी एरर पकड़नी होती है, पर पूरा traceback लॉग या रिपोर्ट के लिए सहेजना होता है। इसके लिए traceback मॉड्यूल है:

import traceback

try:
    1 / 0
except ZeroDivisionError:
    traceback.print_exc()             # stderr में traceback छापता है
    text = traceback.format_exc()     # वही टेक्स्ट स्ट्रिंग में

अगर logging मॉड्यूल से लॉग लिखते हैं, तो except के भीतर logging.exception("राशि की गणना नहीं हो सकी") लिखना आसान है — traceback अपने-आप संदेश के साथ जुड़ जाता है।

Python raise: एक्सेप्शन खुद उठाना

स्टेटमेंट raise एक्सेप्शन को हाथ से उठाता है। इससे फ़ंक्शन बताता है कि ऐसे डेटा के साथ वह काम नहीं कर सकता:

def add_expense(amount):
    if amount < 0:
        raise ValueError(f"राशि ऋणात्मक नहीं हो सकती: {amount}")
    ...

add_expense(-5)
# ValueError: राशि ऋणात्मक नहीं हो सकती: -5

Python raise एक फ़ंक्शन नहीं, स्टेटमेंट है, इसलिए raise के बाद कोष्ठक की ज़रूरत नहीं: कोष्ठक में एक्सेप्शन के कंस्ट्रक्टर को आर्गुमेंट दिए जाते हैं। प्रकार अर्थ के हिसाब से चुनें: गलत मान हो तो ValueError, गलत प्रकार हो तो TypeError।

बिना आर्गुमेंट का raise और raise from

except के भीतर अकेला raise मौजूदा एक्सेप्शन को बिना बदले आगे भेज देता है। यह तब काम आता है, जब आप लॉग में कुछ लिखना चाहते हैं, पर संभालना ऊपर वाले कोड पर छोड़ना चाहते हैं।

अगर एक एक्सेप्शन को दूसरे से बदलना है, तो raise ... from e लिखें — मूल एरर श्रृंखला में बनी रहती है, और traceback में दोनों दिखते हैं:

def load_config(path):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError as e:
        raise RuntimeError(f"कॉन्फ़िग नहीं मिली: {path}") from e

add_note: एरर में संदर्भ जोड़ना

Python 3.11 (PEP 678) से एक्सेप्शन में add_note() मेथड से नोट जोड़ा जा सकता है और फिर उसे आगे उठाया जा सकता है। प्रकार नहीं बदलता, और नोट संदेश के नीचे छपता है:

def parse(raw, line_no):
    try:
        return float(raw)
    except ValueError as e:
        e.add_note(f"फ़ाइल expenses.csv, पंक्ति {line_no}")
        raise

parse("सौ", 17)
# ValueError: could not convert string to float: 'सौ'
# फ़ाइल expenses.csv, पंक्ति 17

अपना एक्सेप्शन कैसे बनाएँ

अपने एक्सेप्शन विषय-क्षेत्र की गलतियों के लिए चाहिए: बजट पार हो गया, ऑर्डर पहले ही भुगतान हो चुका है, उपयोगकर्ता ब्लॉक है। इसके लिए Exception का उत्तराधिकारी क्लास काफ़ी है:

class BudgetExceeded(Exception):
    def __init__(self, category, limit, spent):
        super().__init__(f"{category}: सीमा {limit}, ख़र्च {spent}")
        self.category = category
        self.limit = limit
        self.spent = spent

def check_budget(category, limit, spent):
    if spent > limit:
        raise BudgetExceeded(category, limit, spent)

try:
    check_budget("खाना", 10000, 12500)
except BudgetExceeded as e:
    print("चेतावनी:", e)   # चेतावनी: खाना: सीमा 10000, ख़र्च 12500

अब कॉल करने वाला कोड ठीक बजट के पार होने को पकड़ता है और उसे टाइपो से अलग रखता है। बड़े प्रोजेक्ट में एक साझा बेस क्लास (class TrackerError(Exception)) बनाना सुविधाजनक है, और सारे अपने एक्सेप्शन उससे लें — तब एक except TrackerError से सब पकड़े जा सकते हैं। उत्तराधिकारी Exception से लें, BaseException से नहीं।

एक्सेप्शन ग्रुप और except*

कभी-कभी एक साथ कई एरर होती हैं — जैसे पूरी फ़ाइल जाँचते समय, या asyncio.TaskGroup के समानांतर कार्यों में। Python 3.11 से उन्हें ExceptionGroup में इकट्ठा किया जा सकता है, और प्रकार के अनुसार except* से संभाला जा सकता है:

def check_all(rows):
    errors = []
    for r in rows:
        try:
            float(r)
        except ValueError as e:
            errors.append(e)
    if errors:
        raise ExceptionGroup("फ़ाइल में त्रुटियाँ", errors)

try:
    check_all(["1", "a", "b"])
except* ValueError as eg:
    print(f"ख़राब पंक्तियाँ: {len(eg.exceptions)}")   # ख़राब पंक्तियाँ: 2

Python try except सारी एरर पर: ऐसा क्यों न करें

सबसे नुकसानदेह आदत है सब कुछ पकड़ना: except: pass। प्रोग्राम «गिरना» बंद कर देता है, पर असल में समस्याओं की ख़बर देना बंद कर देता है। खाली except KeyboardInterrupt और SystemExit तक पकड़ लेता है, इसलिए Ctrl+C से प्रोग्राम रोकना मुश्किल हो जाता है। except Exception: pass थोड़ा बेहतर है, पर वह भी नाम के टाइपो और तर्क की गलतियों को चुपचाप निगल जाता है।

एक्सेप्शन सही तरीके से कैसे संभालें:

  • सिर्फ़ वही प्रकार पकड़ें, जिसे आप संभाल सकते हैं: except ValueError, सब कुछ नहीं;
  • try ब्लॉक छोटा रखें — सिर्फ़ वे पंक्तियाँ जो ठीक उसी तरह गिर सकती हैं;
  • अगर शीर्ष स्तर पर चौड़ा पकड़ते हैं (except Exception), तो traceback लॉग करना ज़रूरी है;
  • जहाँ साधारण जाँच काफ़ी हो, वहाँ एक्सेप्शन को सामान्य शाखा की तरह न बरतें: सरल मामलों में try/except KeyError की जगह dict.get();
  • संसाधनों के लिए with लें — वह एरर होने पर भी फ़ाइल बंद कर देता है।

निष्कर्ष

Python के एक्सेप्शन एरर बताने का सामान्य तंत्र हैं, उन्हें छिपाने का साधन नहीं। traceback नीचे से ऊपर पढ़ें, try/except में ख़ास प्रकार पकड़ें, else को «सब ठीक रहा» वाले कोड के लिए और finally को सफ़ाई के लिए लें। Python raise से आप खुद एरर उठाते हैं, raise from और add_note संदर्भ बचाते हैं, और अपने एक्सेप्शन Exception से बनते हैं। कोर्स में यह विषय व्यवहार में सिखाया जाता है: ख़र्च ट्रैकर CSV की एक ख़राब पंक्ति से नहीं गिरता, और बजट पार होने को अपने एक्सेप्शन से बताता है।

अक्सर पूछे जाने वाले प्रश्न

एरर और एक्सेप्शन में Python में क्या फ़र्क है?

बोलचाल में ये समानार्थी हैं। तकनीकी रूप से SyntaxError कोड पढ़ते समय ही मिल जाता है, और बाक़ी एरर चलने के दौरान एक्सेप्शन ऑब्जेक्ट के रूप में उठती हैं, जिन्हें पकड़ा जा सकता है।

एक्सेप्शन के बाद उसी जगह से चलना जारी रह सकता है क्या?

नहीं। पकड़ने के बाद प्रोग्राम try/except के बाद से चलता है, उस पंक्ति से नहीं जहाँ एरर आई। अगर फ़ाइल की हर पंक्ति संभालनी है, तो try को लूप के भीतर रखें।

अगर except के भीतर एक्सेप्शन उठ जाए तो क्या होगा?

वह आगे उठेगा, और मूल एक्सेप्शन उसके __context__ एट्रिब्यूट में सुरक्षित रहेगा। traceback में यह संदेश दिखेगा: During handling of the above exception, another exception occurred.

टेस्ट में कैसे जाँचें कि फ़ंक्शन एक्सेप्शन उठाता है?

pytest में with pytest.raises(ValueError): लिखें, और unittest में self.assertRaises(ValueError)। टेस्ट तभी पास होगा, जब ब्लॉक के भीतर सही प्रकार का एक्सेप्शन उठे।

assert क्या है और raise से कैसे अलग है?

assert शर्त, "संदेश" तब AssertionError उठाता है जब शर्त ग़लत हो। यह विकास के दौरान जाँच का औज़ार है: -O फ़्लैग से चलाने पर assert बंद हो जाते हैं, इसलिए उपयोगकर्ता का इनपुट if और raise से जाँचें।

इस विषय को वीडियो में सीखें

यह विषय “Python 3.15: от новичка до профи” कोर्स में अभ्यास के साथ विस्तार से समझाया गया है।

कोर्स पर जाएँ