पायथन में अपवाद प्रबंधन (Exception Handling)

जो प्रोग्राम पूरी तरह वैध पायथन है, वह भी चलते समय विफल हो सकता है: उपयोगकर्ता संख्या की जगह अक्षर टाइप कर दे, फ़ाइल न मिले, भाजक शून्य निकले। पायथन इनमें से प्रत्येक को एक अपवाद (exception) के रूप में बताता है — एक ऑब्जेक्ट जो बताता है कि क्या गड़बड़ हुई — और यदि प्रोग्राम उसका प्रबंधन न करे तो रुक जाता है। यह अध्याय उन त्रुटियों को, जिन्हें पायथन कुछ भी चलाने से पहले पकड़ लेता है, उन त्रुटियों से अलग करता है जो केवल चलते समय प्रकट होती हैं; उन अंतर्निहित (built-in) अपवादों के नाम बताता है जिन्हें पहचानना अपेक्षित है; और फिर try, except, else और finally से होकर नियंत्रण के प्रवाह को पंक्ति-दर-पंक्ति देखता है। कोड-आउटपुट प्रश्न इसी प्रवाह को परखते हैं, इसलिए अनुमान न लगाएँ, अनुरेखण (trace) करें।

वाक्य-रचना त्रुटियाँ (Syntax Errors) और अपवाद

वाक्य-रचना त्रुटियाँ (syntax errors), जिन्हें पार्सिंग त्रुटियाँ भी कहते हैं, तब पकड़ी जाती हैं जब पायथन कोड पढ़ता है, किसी भी कथन के चलने से पहले: if के बाद कोलन न होना, कोष्ठक बंद न करना, कीवर्ड की ग़लत वर्तनी। पायथन पंक्ति और त्रुटि का प्रकार बताता है, और ठीक होने तक कुछ नहीं चलता। अपवाद अलग है: कोड वाक्य-रचना की दृष्टि से सही है, पर निष्पादन के दौरान कुछ गड़बड़ होती है — शून्य से भाग, ऐसी फ़ाइल खोलना जो है ही नहीं, "abc" को पूर्णांक में बदलना। तब पायथन एक अपवाद-ऑब्जेक्ट बनाता है और यदि कोई उसका प्रबंधन न करे तो प्रोग्राम ट्रेसबैक के साथ अचानक समाप्त हो जाता है। वाक्य-रचना त्रुटियाँ भी SyntaxError नामक अपवाद-वर्ग के रूप में बताई जाती हैं, पर जिस प्रोग्राम में वे हैं वह उन्हें संभाल नहीं सकता, क्योंकि वह प्रोग्राम कभी शुरू ही नहीं होता।

पुस्तक में सूचीबद्ध अंतर्निहित अपवाद, और प्रत्येक किससे उठता है
अपवादकब उठता हैउदाहरण
SyntaxErrorकोड पायथन के व्याकरण को तोड़ता हैif x > 2 (कोलन नहीं)
ValueErrorतर्क का प्रकार सही पर मान अनुपयुक्त हैint("abc"), int("3.5")
TypeErrorकिसी संक्रिया को ग़लत प्रकार के मान पर लगाया गया"5" + 5
ZeroDivisionErrorभाग या शेषफल का भाजक शून्य है10 / 0, 7 % 0
IndexErrorअनुक्रम का सूचकांक सीमा से बाहर है[10, 20, 30][3]
NameErrorकोई चर-नाम परिभाषित नहीं हैprint(totl) जबकि केवल total है
IndentationErrorइंडेंटेशन ग़लत हैलूप का भाग इंडेंट न होना
IOErrorफ़ाइल नहीं खुल पाती (पायथन 3 में OSError का दूसरा नाम; न मिलने वाली फ़ाइल पर FileNotFoundError)open("nofile.txt")
ImportErrorमाँगा गया मॉड्यूल या नाम नहीं मिलताfrom math import sqroot
EOFErrorडेटा पढ़ते समय इनपुट या फ़ाइल का अंत आ जाता हैअंतिम ऑब्जेक्ट के बाद pickle.load()
KeyboardInterruptनिष्पादन के दौरान उपयोगकर्ता इंटरप्ट कुंजी (Ctrl+C) दबाता हैलंबा लूप रोकना
OverflowErrorगणना किसी संख्यात्मक प्रकार की सीमा पार कर जाती हैmath.exp(1000)
⚠️ ValueError या TypeError?
int("3.5") एक ValueError है: int() स्ट्रिंग स्वीकार करता है, अतः प्रकार ठीक है, पर "3.5" वैध पूर्णांक-लिटरल नहीं है। "5" + 5 एक TypeError है: + किसी str और int को जोड़ नहीं सकता, मान चाहे जो हों। पूछें "क्या प्रकार ही ग़लत है, या केवल यह मान?" — और यह जोड़ी भ्रमित करना बंद कर देती है।

अपवाद उठाना: raise और assert

पायथन स्वयं भी अपवाद उठाता है, और प्रोग्राम जानबूझकर भी उठा सकता है। raise कथन का रूप है raise ExceptionName(वैकल्पिक संदेश) — उदाहरण के लिए raise ValueError("marks cannot be negative")। raise के चलते ही वर्तमान खंड का शेष भाग छोड़ दिया जाता है और पायथन किसी हैंडलर की खोज करता है। assert कथन उस शर्त की जाँच करता है जिसके सत्य होने की प्रोग्रामर अपेक्षा करता है: assert expression, message। व्यंजक True हो तो कुछ नहीं होता; False हो तो पायथन संदेश सहित AssertionError उठाता है। assert इनपुट या किसी मान्यता की त्वरित जाँच है, जैसे वर्गमूल लेने से पहले संख्या का ऋणेतर होना।

उदाहरण के लिए, यह अंश पहले 13 और फिर 4 छापता है — raise के बाद की पंक्ति कभी नहीं चलती:
try:
print("1", end="")
raise IndexError
print("2", end="")
except IndexError:
print("3", end="")
print("4")

अपवादों का प्रबंधन: try और except

त्रुटि होने पर पायथन एक अपवाद-ऑब्जेक्ट बनाकर उसे रनटाइम सिस्टम को सौंपता है — इसे अपवाद को फेंकना (throwing) कहते हैं। रनटाइम सिस्टम फिर उन कॉल्स में पीछे की ओर खोजता है जिनसे त्रुटि तक पहुँचा गया, ताकि उससे निपटने वाला कोड, अपवाद हैंडलर, मिल सके; उसका मिल जाना अपवाद को पकड़ना (catching) कहलाता है। कहीं भी हैंडलर न मिले तो प्रोग्राम रुक जाता है। जिस कोड के विफल होने की संभावना हो उसे try खंड में रखा जाता है, और हैंडलर को except खंड में, जो उस अपवाद का नाम लेता है जिसे वह संभालता है। try खंड बिना त्रुटि चल जाए तो हर except खंड छोड़ दिया जाता है।

  • एक try के बाद कई except खंड हो सकते हैं, प्रत्येक अपवाद के लिए एक; पायथन पहला मेल खाने वाला खंड चलाता है और शेष छोड़ देता है।
  • बिना अपवाद-नाम वाला except हर अपवाद पकड़ता है। इसे अंतिम except खंड होना चाहिए — पहले रखना स्वयं एक SyntaxError है ("default 'except:' must be last")।
  • बिना मेल वाला अपवाद — जिसका नाम किसी except खंड में न हो — यहाँ नहीं संभलता; वह बाहर कॉलर तक जाता है, और कोई न पकड़े तो प्रोग्राम समाप्त हो जाता है।

एक हल किया गया अनुरेखण। प्रत्येक मद बारी-बारी से आज़माई जाती है; "x" पूर्णांक नहीं बन सकता और "0" से भाग विफल होता है, अतः योग 3 + 100 + 2 + 1000 है और प्रोग्राम 1105 छापता है:
total = 0
for item in ["4", "x", "6", "0"]:
try:
total += 12 // int(item)
except ValueError:
total += 100
except ZeroDivisionError:
total += 1000
print(total)

else और finally खंड

try कथन में दो और खंड हो सकते हैं। else खंड केवल तब चलता है जब try खंड में कोई अपवाद न उठा हो; यह उस कोड का स्थान है जो जोखिम वाला भाग सफल होने के बाद चलना चाहिए। finally खंड सदैव चलता है — try खंड सफल रहा हो, अपवाद पकड़ा गया हो, या पकड़ा ही न गया हो — इसलिए सफ़ाई का काम, जैसे फ़ाइल बंद करना, यहीं होता है। अपवाद न संभला हो तो भी finally पहले चलता है और फिर अपवाद आगे भेजा जाता है। पृष्ठ पर क्रम निश्चित है: try, फिर except खंड, फिर else, फिर finally।

कौन-से खंड चलते हैं
try में क्या होता हैexceptelsefinally
कोई अपवाद नहींछोड़ा जाता हैचलता हैचलता है
अपवाद किसी except से मेल खाता हैमेल वाला चलता हैछोड़ा जाता हैचलता है
अपवाद किसी से मेल नहीं खाताकोई नहीं चलताछोड़ा जाता हैचलता है, फिर अपवाद आगे जाता है
⚠️ finally, return के बाद भी चलता है
किसी फ़ंक्शन में try या except के भीतर का return, finally को नहीं छोड़ता। नीचे के अंश में f(6, 0) return -1 तक पहुँचता है, पर मान लौटने से पहले finally "done " छापता है, अतः छपी पंक्ति done -1 है:
def f(a, b):
try:
return a / b
except ZeroDivisionError:
return -1
finally:
print("done", end=" ")
print(f(6, 0))
🧠 तीन प्रश्नों से अनुरेखण
किसी भी try कथन के लिए क्रम से पूछें: (1) try की कौन-सी पंक्ति, यदि कोई, अपवाद उठाती है — और क्या उससे पहले कुछ छपा? (2) कौन-सा except, यदि कोई, मेल खाता है? (3) क्या try पूरा साफ़ चला, ताकि else चले? हर स्थिति में finally अंत में छपता है।

मुख्य बिंदु

  • वाक्य-रचना त्रुटियाँ कोड पार्स होते समय पकड़ी जाती हैं, अतः कुछ नहीं चलता; अपवाद वाक्य-रचना की दृष्टि से वैध कोड के निष्पादन के दौरान उठते हैं।
  • ValueError: प्रकार सही, मान ग़लत (int("3.5")); TypeError: प्रकार ग़लत ("5" + 5); IndexError, NameError, ZeroDivisionError, EOFError अपने कारण का नाम लेते हैं।
  • raise जानबूझकर अपवाद फेंकता है और खंड का शेष भाग छोड़ देता है; assert की शर्त False हो तो AssertionError उठता है।
  • पायथन पहला मेल खाने वाला except चलाता है; बिना नाम का except सब कुछ पकड़ता है और उसे अंत में होना चाहिए।
  • else केवल तब चलता है जब try में कुछ न उठा हो; finally हर स्थिति में चलता है, return के बाद भी, और किसी असंभले अपवाद के आगे जाने से पहले भी।
  • फेंकना अपवाद-ऑब्जेक्ट बनाना है; पकड़ना रनटाइम सिस्टम का उसके लिए हैंडलर ढूँढ लेना है।

अभ्यास प्रश्न (8)

उत्तर खोलने से पहले प्रत्येक प्रश्न हल करें। हर व्याख्या सही विकल्प के साथ लुभावना गलत विकल्प भी बताती है, क्योंकि अंक वहीं जाते हैं।

  1. निम्नलिखित कोड का आउटपुट क्या है?
    try:
    x = int("12a")
    print("A", end="")
    except ValueError:
    print("B", end="")
    else:
    print("C", end="")
    finally:
    print("D", end="")

    1. ABD
    2. BD
    3. BCD
    4. ACD
    उत्तर देखें

    उत्तर: B — BD

    int("12a") try की पहली पंक्ति पर ValueError उठाता है, अतः print("A") कभी नहीं चलता; मेल वाला except B छापता है, else छोड़ा जाता है क्योंकि try साफ़ पूरा नहीं हुआ, और finally D छापता है — BD। ABD भूल जाता है कि अपवाद उठते ही try का शेष भाग छोड़ दिया जाता है, और BCD else चलाता है, जो तभी होता है जब try में कुछ न उठे।
  2. पायथन 3 में व्यंजक int("3.5") कौन-सा अपवाद उठाता है?

    1. TypeError
    2. SyntaxError
    3. NameError
    4. ValueError
    उत्तर देखें

    उत्तर: D — ValueError

    int() स्ट्रिंग तर्क स्वीकार करता है, अतः प्रकार स्वीकार्य है; विफल होता है मान, क्योंकि "3.5" वैध पूर्णांक-लिटरल नहीं है — यह ValueError है। TypeError भ्रामक विकल्प है: वह तब उठता जब प्रकार ही अनुपयोगी होता, जैसे "5" + 5 में। पंक्ति व्याकरण की दृष्टि से वैध है, अतः यह SyntaxError नहीं है, और इसमें हर नाम परिभाषित है।
  3. किसी try कथन में else खंड कब निष्पादित होता है?

    1. केवल तब जब try खंड बिना कोई अपवाद उठाए पूरा हो
    2. केवल तब जब अपवाद उठे और किसी except खंड द्वारा पकड़ा जाए
    3. सदैव, try खंड के बाद और finally से पहले
    4. केवल तब जब उठे अपवाद से कोई except खंड मेल न खाए
    उत्तर देखें

    उत्तर: A — केवल तब जब try खंड बिना कोई अपवाद उठाए पूरा हो

    else सफलता की शाखा है: यह केवल तब चलता है जब try खंड बिना अपवाद पूरा हो, जिससे उस सफलता पर निर्भर कोड try खंड से बाहर रहता है। "सदैव" finally का वर्णन है, else का नहीं। अपवाद पकड़ा जाए तो else के स्थान पर except खंड चलता है, और कोई मेल न खाए तो न except चलता है न else — केवल finally, अपवाद के आगे जाने से पहले।
  4. निम्नलिखित कोड क्या छापता है?
    def f(a, b):
    try:
    return a / b
    except ZeroDivisionError:
    return -1
    finally:
    print("done", end=" ")
    print(f(6, 0))

    1. -1
    2. -1 done
    3. done -1
    4. done
    उत्तर देखें

    उत्तर: C — done -1

    6 / 0 ZeroDivisionError उठाता है, except खंड return -1 तक पहुँचता है, और फ़ंक्शन के वास्तव में लौटने से पहले finally फिर भी चलता है, जो स्पेस सहित "done " छापता है। उसके बाद ही बाहरी print को -1 मिलता है और वह उसे छापता है, अतः done -1। केवल -1 लिखना मानता है कि return, finally को छोड़ देता है, और "-1 done" क्रम उलट देता है, मानो फ़ंक्शन पूरा होने से पहले मान छप गया हो।
  5. मान लीजिए: x = -3 के बाद assert x >= 0, "negative"। assert कथन चलने पर क्या होता है?

    1. यह "negative" छापता है और निष्पादन सामान्य रूप से चलता रहता है
    2. यह "negative" संदेश सहित ValueError उठाता है
    3. यह "negative" संदेश सहित AssertionError उठाता है
    4. यह x को 0 कर देता है ताकि शर्त सत्य हो जाए
    उत्तर देखें

    उत्तर: C — यह "negative" संदेश सहित AssertionError उठाता है

    assert अपनी शर्त का मूल्यांकन करता है; -3 >= 0 False है, अतः पायथन AssertionError उठाता है और कॉमा के बाद वाला संदेश जोड़ देता है। अपने-आप कुछ नहीं छपता — अपवाद न संभले तो प्रोग्राम AssertionError: negative पर समाप्त होने वाले ट्रेसबैक के साथ रुक जाता है। ValueError एक अलग अपवाद है जिसे assert कभी नहीं उठाता, और assert केवल शर्त जाँचता है; वह कभी किसी चर को नहीं बदलता।
  6. अभिकथन (A): जिस पायथन स्क्रिप्ट की अंतिम पंक्ति में वाक्य-रचना त्रुटि हो, वह कुछ भी नहीं छापती, त्रुटि से ऊपर की पंक्तियों से भी नहीं। कारण (R): वाक्य-रचना त्रुटियाँ कोड पार्स होते समय, किसी भी कथन के निष्पादन से पहले, पकड़ी जाती हैं।

    1. A और R दोनों सत्य हैं, और R, A की सही व्याख्या है
    2. A और R दोनों सत्य हैं, पर R, A की सही व्याख्या नहीं है
    3. A सत्य है, पर R असत्य है
    4. A असत्य है, पर R सत्य है
    उत्तर देखें

    उत्तर: A — A और R दोनों सत्य हैं, और R, A की सही व्याख्या है

    पायथन चलाने से पहले पूरी स्क्रिप्ट पढ़ता और पार्स करता है, अतः कहीं भी वाक्य-रचना त्रुटि हो तो प्रोग्राम शुरू ही नहीं होता; दोषपूर्ण पंक्ति से ऊपर के print कथनों तक कभी नहीं पहुँचा जाता। वही पार्सिंग चरण A का कारण है, अतः R, A की व्याख्या करता है। A को असत्य मानना वाक्य-रचना त्रुटियों को अपवादों से उलझाना है, जो पहले की पंक्तियों को चलने देते हैं क्योंकि वे केवल निष्पादन के दौरान उठते हैं।
  7. एक प्रोग्रामर एक ही try के अंतर्गत पहले बिना नाम का except: खंड और उसके बाद except ValueError: खंड लिखता है। क्या होता है?

    1. ValueError दूसरे खंड से संभलता है, बाक़ी सब पहले से
    2. जब भी अपवाद हो, दोनों खंड एक के बाद एक चलते हैं
    3. कोड चलता है, और दूसरे खंड तक कभी पहुँचा ही नहीं जाता
    4. पायथन कोड चलाने से मना करता है: बिना नाम का except अंतिम खंड होना चाहिए
    उत्तर देखें

    उत्तर: D — पायथन कोड चलाने से मना करता है: बिना नाम का except अंतिम खंड होना चाहिए

    बिना नाम का except हर अपवाद पकड़ता है, अतः उसके बाद के किसी खंड तक कभी पहुँचा नहीं जा सकता; पायथन इस क्रम को कोड की ही त्रुटि मानता है और कुछ भी चलाने से पहले SyntaxError: default 'except:' must be last बताता है। "बाद के खंड तक बस पहुँचा नहीं जाता" वाला उत्तर लुभाता है, पर पायथन इसे चलते समय खोजने की प्रतीक्षा नहीं करता, और किसी अपवाद के लिए केवल एक ही except खंड चलता है।
  8. अपवाद प्रबंधन में किसी अपवाद को "पकड़ने (catching)" से क्या अभिप्राय है?

    1. त्रुटि होने पर अपवाद-ऑब्जेक्ट बनाना
    2. रनटाइम सिस्टम का उसके लिए उपयुक्त हैंडलर ढूँढकर चलाना
    3. प्रोग्राम चलने से पहले वाक्य-रचना त्रुटि पकड़ना
    4. त्रुटि-संदेश को डिस्क की किसी लॉग फ़ाइल में लिखना
    उत्तर देखें

    उत्तर: B — रनटाइम सिस्टम का उसके लिए उपयुक्त हैंडलर ढूँढकर चलाना

    त्रुटि होने पर अपवाद-ऑब्जेक्ट बनाकर रनटाइम सिस्टम को सौंपना फेंकना है; फिर रनटाइम सिस्टम उसे संभाल सकने वाले कोड की खोज करता है, और उस हैंडलर को ढूँढकर चलाना पकड़ना है। ऑब्जेक्ट बनाना फेंकना है, प्रक्रिया का दूसरा छोर। वाक्य-रचना त्रुटियाँ पार्सिंग के दौरान मिलती हैं और प्रोग्राम उन्हें पकड़ता ही नहीं, और पकड़ने का किसी लॉग फ़ाइल से कोई संबंध नहीं।