அடிப்படை

Python-இல் விதிவிலக்குகள் (exceptions): try, except, else, finally, raise மற்றும் சொந்த விதிவிலக்குகள்

புதுப்பிக்கப்பட்டது: · Skillaria.Top

Python-இல் exception என்பது பிழையைக் குறிக்கும் பொருள்; அது try/except தொகுதியால் பிடிக்கப்படும் வரை வழக்கமான இயக்கத்தை நிறுத்துகிறது. else பிழை இல்லாதபோது இயங்கும், finally எப்போதும் இயங்கும். raise கட்டளையால் நாமே exception-ஐ எழுப்பலாம், எடுத்துக்காட்டாக raise ValueError("தொகை எதிர்மறை"); சொந்த exception Exception-இன் துணைவகுப்பால் உருவாக்கப்படுகிறது.

Python-இல் exception என்பது பிழையைச் சமிக்ஞை செய்து சாதாரண நிரல் ஓட்டத்தை நிறுத்தும் பொருள். யாரும் பிடிக்கவில்லையென்றால் Python traceback-ஐ அச்சிட்டு நிரலை முடிக்கும். try/except கட்டமைப்பால் அவற்றைப் பிடிக்கிறோம்; raise கட்டளையால் நாமே எழுப்புகிறோம். இது எப்படி அமைந்துள்ளது, எந்தப் பிழைகள் அடிக்கடி வருகின்றன, பிழை கையாளுதலை பிழைகளை மறைப்பதாக மாற்றாமல் எப்படி எழுதுவது என்று பார்ப்போம்.

Python-இல் exception என்றால் என்ன

ஒரு செயல்பாடு நிறைவேற முடியாதபோது — பூஜ்ஜியத்தால் வகுத்தல், இல்லாத விசையை அணுகுதல், சொல்லை எண்ணாக மாற்றுதல் — Python ஒரு exception பொருளை உருவாக்கி "எழுப்புகிறது". தற்போதைய செயல்பாடு நிற்கும், விதிவிலக்கு அழைப்புச் சங்கிலியின் வழியே மேலே ஏறும், பொருத்தமான கையாளுநரைக் கண்டறியும் வரை. கிடைக்கவில்லையென்றால் நிரல் விழுகிறது.

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

அடிக்கடி கேட்கப்படும் கேள்விக்கு பதில்: Python-இல் பூஜ்ஜியத்தால் வகுக்கும்போது ZeroDivisionError எழுகிறது. இது /, // மற்றும் % மூன்றுக்கும் பொருந்தும்.

Traceback-ஐ எப்படிப் படிப்பது

Traceback என்பது நிரல் பிழையை அடைந்த வழியின் அறிக்கை. அதை கீழிருந்து மேலாகப் படிப்பது எளிது. கடைசி வரி முக்கியம்: exception-இன் வகையும் செய்தியும். அதற்கு மேல் பிழை நடந்த குறியீட்டு வரி, அதற்கு மேல் அதை அழைத்தது யார் — நுழைவுப் புள்ளி வரை.

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 குறியீட்டின் பிழை அல்ல: பயனர் Ctrl+C அழுத்தியதை Python இப்படித்தான் தெரிவிக்கிறது. அது வேண்டுமென்றே 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 தொகுதிகளை எழுதலாம், அல்லது வகைகளை ஒரு tuple-இல் பட்டியலிடலாம்:

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 இருந்தால் அடைப்புகள் இன்னும் தேவை.

பிழைச் செய்தியையும் அதன் வகையையும் அறிவது

Exception பொருள் 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()             # traceback-ஐ stderr-க்கு அச்சிடும்
    text = traceback.format_exc()     # அதே உரை சரமாக

logging தொகுதி மூலம் பதிவு எழுதினால், except-க்குள் logging.exception("கணக்கிட முடியவில்லை") அழைப்பது எளிது — traceback செய்தியுடன் தானாகச் சேர்க்கப்படும்.

Python raise: விதிவிலக்கை எழுப்புவது

raise கட்டளை exception-ஐ கைமுறையாக எழுப்புகிறது. அப்படித்தான் ஒரு செயல்பாடு இத்தகைய தரவுடன் வேலை செய்ய முடியாது என்று அறிவிக்கிறது:

def add_expense(amount):
    if amount < 0:
        raise ValueError(f"தொகை எதிர்மறையாக இருக்க முடியாது: {amount}")
    ...

add_expense(-5)
# ValueError: தொகை எதிர்மறையாக இருக்க முடியாது: -5

Python raise ஒரு செயல்பாடல்ல, ஒரு கட்டளை; அதனால் raise என்ற சொல்லுக்குப் பின் அடைப்புகள் தேவையில்லை: அடைப்புக்குள் அனுப்பப்படுவது exception கட்டமைப்பிக்குரிய வாதங்கள். வகையைப் பொருளின்படித் தேர்ந்தெடுங்கள்: தவறான மதிப்பு — 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) exception-க்கு 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

Python-இல் சொந்த exception உருவாக்குவது

சொந்த விதிவிலக்குகள் துறையின் பிழைகளுக்குத் தேவை: பட்ஜெட் மீறப்பட்டது, ஆர்டர் ஏற்கனவே செலுத்தப்பட்டது, பயனர் தடுக்கப்பட்டார். 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-இலிருந்து அல்ல.

Exception குழுக்கள் மற்றும் 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 சற்று மேல்தான், ஆனால் அதுவும் பெயர் எழுத்துப்பிழைகளையும் தர்க்கப் பிழைகளையும் அமைதியாக விழுங்கும்.

Python-இல் விதிவிலக்குகளை சரியாகக் கையாள:

  • கையாளத் தெரிந்த வகையை மட்டும் பிடியுங்கள்: except ValueError, எல்லாவற்றையும் அல்ல;
  • try தொகுதியை சிறியதாக வையுங்கள் — அப்படித்தான் விழக்கூடிய வரிகள் மட்டும் அதில் இருக்கும்;
  • நிரலின் மேல் நிலையில் பரந்த பிடிப்பு (except Exception) பயன்படுத்தினால், traceback-ஐ கட்டாயம் பதிவு செய்யுங்கள்;
  • எளிய சரிபார்ப்பு போதுமானதாக இருக்கும் இடத்தில் விதிவிலக்குகளை சாதாரண கிளைக்கு பயன்படுத்தாதீர்கள்: எளிய நிலைகளில் try/except KeyError-க்குப் பதிலாக dict.get();
  • வளங்களுக்கு with-ஐ விரும்புங்கள் — பிழை வந்தாலும் அது கோப்பை மூடிவிடும்.

சுருக்கம்

Python-இல் exceptions பிழைகளைத் தெரிவிக்கும் சாதாரண வழிமுறை, அவற்றை மறைக்க அல்ல. Traceback-ஐ கீழிருந்து மேலாகப் படியுங்கள், try/except-இல் குறிப்பான வகைகளைப் பிடியுங்கள், வெற்றிக்கான குறியீட்டுக்கு else-ஐயும், சுத்தம் செய்வதற்கு finally-ஐயும் பயன்படுத்துங்கள். Python raise பிழையை நாமே தெரிவிக்க உதவும்; raise from மற்றும் add_note சூழலைப் பாதுகாக்கும்; சொந்த விதிவிலக்குகள் Exception-இலிருந்து மரபுரிமை பெறும். Python பாடநெறியில் இந்தத் தலைப்பு நடைமுறையில் விளக்கப்பட்டுள்ளது: செலவு கண்காணிப்பான் ஒரு தவறான CSV வரியால் விழுவதில்லை, பட்ஜெட் மீறலை அதன் சொந்த விதிவிலக்கால் வெளிப்படுத்துகிறது.

அடிக்கடி கேட்கப்படும் கேள்விகள்

பிழைக்கும் விதிவிலக்குக்கும் என்ன வேறுபாடு Python-இல்?

பேச்சு வழக்கில் இவை ஒத்த சொற்கள். தொழில்நுட்ப ரீதியாக SyntaxError குறியீட்டை அலசும்போதே கண்டறியப்படுகிறது; மற்ற பிழைகள் இயக்கத்தின்போது பிடிக்கக்கூடிய exception பொருள்களாக எழுகின்றன.

விதிவிலக்கு நிகழ்ந்த அதே இடத்திலிருந்து நிரலைத் தொடர முடியுமா?

இல்லை. பிடித்த பின் இயக்கம் try/except தொகுதிக்குப் பின்னால் தொடரும், பிழை நடந்த வரியிலிருந்து அல்ல. கோப்பின் ஒவ்வொரு வரியையும் கையாள வேண்டுமென்றால் try-ஐ சுழலுக்குள் வையுங்கள்.

<code>except</code>-க்குள் விதிவிலக்கு எழுந்தால் என்ன ஆகும்?

அது மேலே செல்லும்; அசல் விதிவிலக்கு புதியதன் <code>__context__</code> பண்புக்கூறில் தக்கவைக்கப்படும். traceback-இல் "During handling of the above exception, another exception occurred" என்ற வாசகம் தோன்றும்.

ஒரு செயல்பாடு விதிவிலக்கை எழுப்புகிறது என்று சோதனைகளில் எப்படி உறுதிப்படுத்துவது?

pytest-இல் <code>with pytest.raises(ValueError):</code>, unittest-இல் <code>self.assertRaises(ValueError)</code>. தொகுதிக்குள் சரியான வகையுடன் விதிவிலக்கு எழுந்தால் மட்டுமே சோதனை தேறும்.

assert என்றால் என்ன, அது raise-இலிருந்து எப்படி வேறுபடுகிறது?

<code>assert நிபந்தனை, "செய்தி"</code> நிபந்தனை பொய்யானால் AssertionError எழுப்பும். இது மேம்பாட்டின்போதான சரிபார்ப்புக்கான கருவி: <code>-O</code> கொடியுடன் இயக்கினால் assert-கள் முடக்கப்படுகின்றன; அதனால் பயனர் உள்ளீட்டை if மற்றும் raise மூலம் சரிபார்க்க வேண்டும்.

இந்தத் தலைப்பை வீடியோவில் கற்றுக்கொள்ளுங்கள்

“Python 3.15: от новичка до профи” பாடநெறியில் இந்தத் தலைப்பு பயிற்சியுடன் விரிவாக விளக்கப்பட்டுள்ளது.

பாடநெறிக்குச் செல்