Python-இல் விதிவிலக்குகள் (exceptions): try, except, else, finally, raise மற்றும் சொந்த விதிவிலக்குகள்
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: от новичка до профи” பாடநெறியில் இந்தத் தலைப்பு பயிற்சியுடன் விரிவாக விளக்கப்பட்டுள்ளது.
பாடநெறிக்குச் செல்