Exceptions katika Python: try, except, else, finally, raise na exceptions zako mwenyewe
Exception katika Python ni kitu kinachoashiria kosa na kusimamisha mtiririko wa kawaida wa msimbo hadi kishughulikiaji cha try/except kiupate; else hutekelezwa kama hakukuwa na kosa, na finally hutekelezwa kila mara. Kosa linaweza kuinuliwa mwenyewe kwa amri raise, kwa mfano raise ValueError("kiasi ni hasi"), na exception yako mwenyewe huundwa kwa darasa linalorithi kutoka Exception.
Exception katika Python ni kitu kinachoashiria kosa na kusimamisha mtiririko wa kawaida wa programu. Ikiwa hakuna aliyeikamata, Python huchapisha traceback na kumaliza kazi. Exceptions hukamatwa kwa muundo wa try/except, na huinuliwa kwa amri raise. Hebu tuchunguze jinsi hili linavyofanya kazi, ni makosa gani yanayotokea mara nyingi, na jinsi ya kushughulikia makosa bila kuyaficha.
Exception katika Python ni nini
Operesheni inaposhindwa kutekelezwa — kugawanya kwa sifuri, kufikia ufunguo usiokuwepo kwenye kamusi, kubadilisha neno kuwa namba — Python huunda kitu cha exception na kukitupa. Kazi ya sasa inasimama, exception inapanda kupitia mnyororo wa miito hadi ikutane na kishughulikiaji kinachofaa. Usipokutana nacho, programu inaanguka.
>>> 10 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
Jibu la swali linaloulizwa mara nyingi: kugawanya kwa sifuri katika Python kunasababisha exception ZeroDivisionError. Hii inahusu /, // na %.
Jinsi ya kusoma traceback
Traceback ni ripoti ya jinsi programu ilivyofika kwenye kosa. Ni rahisi kuisoma kutoka chini kwenda juu. Mstari wa mwisho ndio muhimu zaidi: aina ya exception na ujumbe. Juu yake kuna mstari wa msimbo ambapo kosa lilitokea, na juu zaidi — aliyeuita, na hivyo hadi mahali pa kuingia.
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: 'mia'
Kuanzia Python 3.11 chini ya mstari wa msimbo huchorwa alama ~~~^^^ (PEP 657): zinaonyesha ni usemi upi hasa kwenye mstari ulioharibika. Hii inasaidia sana pale ambapo mstari mmoja una miito kadhaa mfululizo.
Mapendekezo ya "Did you mean"
Kuanzia Python 3.10, kosa la jina lenye herufi zilizopotoshwa linapendekeza jina lililo karibu. Katika Python 3.15 mapendekezo yamekuwa bora zaidi: mkalimani anajua tabia kutoka lugha nyingine. Mwito wa list.push utapendekeza append, na "kahawa".toUpperCase() — mbinu upper. Pendekezo pia linaangalia sehemu zilizoingizwa ndani: ikiwa kamusi iko kwenye sifa totals ya kitu, na umeita items() moja kwa moja kwenye kitu, kosa litapendekeza .totals.items. Majina yaliyo karibu sasa pia hupendekezwa na delattr().
Aina za exceptions katika Python
Exceptions zilizojengwa ndani ni dazeni, lakini mwanzoni utakutana zaidi na hizi:
| Exception | Inatokea lini | Mfano |
|---|---|---|
NameError | Jina halijafafanuliwa, mara nyingi ni kosa la kuandika | print(totl) |
TypeError | Operesheni haifai kwa aina hiyo | "5" + 3 |
ValueError | Aina ni sahihi, thamani si sahihi | int("mia") |
KeyError | Hakuna ufunguo huo kwenye kamusi | d["missing"] |
IndexError | Faharasa nje ya orodha | [1, 2][5] |
AttributeError | Kitu hakina mbinu au sehemu hiyo | [].push(1) |
ZeroDivisionError | Kugawanya kwa sifuri | 1 / 0 |
FileNotFoundError | Faili halijapatikana | open("nope.txt") |
Kuna SyntaxError (na kesi yake maalum IndentationError): hutokea tayari wakati faili linachambuliwa, kabla msimbo haujaendeshwa, kwa hiyo haiwezi kukamatwa kwa try katika faili hilohilo.
Mpangilio wa exceptions
Exceptions ni madarasa, na yamepangwa katika mpangilio wa urithi. Juu kabisa kuna BaseException. Kutoka hapo wanarithi Exception (karibu makosa yote ya kawaida) na zile za mfumo: KeyboardInterrupt, SystemExit, GeneratorExit.
BaseException
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ └── ZeroDivisionError
├── LookupError
│ ├── KeyError
│ └── IndexError
├── OSError
│ └── FileNotFoundError
├── TypeError
└── ValueError
Mpangilio huu ni muhimu unapokamata: except LookupError itakamata KeyError na IndexError. Na KeyboardInterrupt si kosa la msimbo: hivyo Python inaonyesha kwamba mtumiaji amebonyeza Ctrl+C. Haitokani na Exception kwa makusudi, ili except Exception isizuie kusimamisha programu.
Kushughulikia exceptions: try/except/else/finally
Msimbo ambao unaweza kuanguka huwekwa kwenye kizuizi cha try. Katika except unataja aina ya kosa na nini cha kufanya nalo. Ni except ndicho kizuizi ambacho Python hutumia kushughulikia exceptions.
def parse_amount(raw):
try:
value = float(raw)
except ValueError as e:
print(f"Ninaruka mstari: {e}")
return None
else:
return value # hutekelezwa ikiwa hakukuwa na kosa
finally:
print("uchambuzi umekamilika") # hutekelezwa kila mara
print(parse_amount("250"))
Hapa mpangilio wa matokeo ni: kwanza "uchambuzi umekamilika", kisha 250.0. return ndani ya else inakumbuka thamani, kisha Python lazima itekeleze finally na ndipo irudishe matokeo. Kwa hiyo finally inaaminika kwa usafishaji: kufunga faili, kuachilia muunganisho.
Ukiandika return yako mwenyewe ndani ya finally, itafuta ile ya awali na hata "kumeza" exception. Kuanzia Python 3.14 msimbo kama huo unatoa SyntaxWarning (PEP 765) — usiandike return ndani ya finally.
Exceptions kadhaa kwa mara moja
Unaweza kuandika vizuizi kadhaa vya except kwa aina tofauti, au kuorodhesha aina kwenye tuple:
try:
amount = int(data["amount"])
except KeyError:
amount = 0
except (ValueError, TypeError) as e:
print("Kiasi si sahihi:", e)
amount = 0
Vizuizi hukaguliwa kutoka juu kwenda chini, na la kwanza linalofaa ndilo linalotekelezwa, kwa hiyo aina mahususi huwekwa juu ya zile za jumla. Kuanzia Python 3.14 (PEP 758) aina kadhaa zinaweza kuorodheshwa bila mabano — except ValueError, TypeError: — lakini ikiwa kuna as, mabano bado yanahitajika.
Jinsi ya kuonyesha ujumbe wa kosa na kujua aina yake
Kitu cha exception kinapatikana kupitia as e. str(e) hutoa ujumbe, na type(e).__name__ hutoa jina la aina:
try:
int("mia")
except ValueError as e:
print(type(e).__name__) # ValueError
print(e) # invalid literal for int() with base 10: 'mia'
Moduli ya traceback: maandishi kamili ya kosa
Wakati mwingine kosa linahitaji kukamatwa lakini traceback kamili ihifadhiwe — kwa logi au ripoti. Kwa hilo kuna moduli traceback:
import traceback
try:
1 / 0
except ZeroDivisionError:
traceback.print_exc() # inachapisha traceback kwenye stderr
text = traceback.format_exc() # maandishi hayohayo kama mfuatano
Ikiwa unaandika logi kwa moduli logging, ni rahisi kuita logging.exception("imeshindwa kuhesabu") ndani ya except — traceback itaongezwa kwenye ujumbe moja kwa moja.
Python raise: jinsi ya kuinua exception
Amri raise inatupa exception kwa mkono. Kwa njia hii kazi inaeleza kwamba haiwezi kufanya kazi na data kama hizo:
def add_expense(amount):
if amount < 0:
raise ValueError(f"Kiasi hakiwezi kuwa hasi: {amount}")
...
add_expense(-5)
# ValueError: Kiasi hakiwezi kuwa hasi: -5
Python raise ni amri, si kazi, kwa hiyo hakuna haja ya mabano baada ya neno raise: ndani ya mabano unapitisha hoja za konstrukta ya exception. Chagua aina kwa maana yake: thamani isiyo sahihi ni ValueError, aina isiyo sahihi ni TypeError.
raise bila hoja na raise from
Ndani ya except, raise tupu hupeleka mbele exception ya sasa bila mabadiliko. Hii inahitajika ukitaka kuandika kitu kwenye logi lakini uache ushughulikiaji kwa msimbo wa mwito.
Ukibadilisha exception moja na nyingine, tumia raise ... from e — kosa la asili linabaki kwenye mnyororo, na traceback inaonyesha zote mbili:
def load_config(path):
try:
with open(path) as f:
return f.read()
except FileNotFoundError as e:
raise RuntimeError(f"Hakuna config {path}") from e
add_note: kuongeza muktadha kwenye kosa
Kuanzia Python 3.11 (PEP 678) unaweza kuongeza maelezo kwenye exception kwa mbinu add_note() na kuendelea kuirusha. Aina ya kosa haibadiliki, na maelezo yanachapishwa chini ya ujumbe:
def parse(raw, line_no):
try:
return float(raw)
except ValueError as e:
e.add_note(f"faili expenses.csv, mstari {line_no}")
raise
parse("mia", 17)
# ValueError: could not convert string to float: 'mia'
# faili expenses.csv, mstari 17
Jinsi ya kuunda exception yako katika Python
Exceptions zako mwenyewe zinahitajika kwa makosa ya eneo la biashara: bajeti imezidi, agizo tayari limelipwa, mtumiaji amezuiwa. Inatosha kuwa na darasa linalorithi Exception:
class BudgetExceeded(Exception):
def __init__(self, category, limit, spent):
super().__init__(f"{category}: kikomo {limit}, kimetumika {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("Chakula", 10000, 12500)
except BudgetExceeded as e:
print("Onyo:", e) # Onyo: Chakula: kikomo 10000, kimetumika 12500
Sasa msimbo wa mwito unakamata hasa kuzidi kwa bajeti, na hauchanganyi na makosa ya kuandika. Katika mradi mkubwa ni vizuri kuwa na darasa la msingi la pamoja (class TrackerError(Exception)) na kurithi exceptions zako zote kutoka humo — basi zote zinaweza kukamatwa kwa except TrackerError moja. Rithi kutoka Exception, si kutoka BaseException.
Vikundi vya exceptions na except*
Wakati mwingine kuna makosa kadhaa kwa wakati mmoja — kwa mfano wakati wa kukagua faili zima, au katika kazi sambamba za asyncio.TaskGroup. Kuanzia Python 3.11 yanaweza kukusanywa kwenye ExceptionGroup, na kushughulikiwa kwa aina kupitia except*:
def check_all(rows):
errors = []
for r in rows:
try:
float(r)
except ValueError as e:
errors.append(e)
if errors:
raise ExceptionGroup("makosa katika faili", errors)
try:
check_all(["1", "a", "b"])
except* ValueError as eg:
print(f"Mistari yenye hitilafu: {len(eg.exceptions)}") # Mistari yenye hitilafu: 2
Python try except kwa makosa yote: kwa nini hilo si wazo zuri
Tabia mbaya zaidi ni kukamata kila kitu: except: pass. Programu "inaacha kuanguka", lakini kwa kweli inaacha kuripoti matatizo. except tupu hukamata hata KeyboardInterrupt na SystemExit, kwa hiyo programu haisimamishwi kwa Ctrl+C. except Exception: pass ni bora kidogo, lakini bado inameza kimya makosa ya kuandika majina na makosa ya mantiki.
Jinsi ya kushughulikia exceptions kwa usahihi katika Python:
- kamata aina ile tu unayoweza kuishughulikia:
except ValueError, si kila kitu; - weka kizuizi cha
trykifupi — mistari tu inayoweza kuanguka kwa namna hiyo; - ukikamata kwa upana (
except Exception) kwenye ngazi ya juu ya programu, hakikisha unaandika traceback kwenye logi; - usitumie exceptions kwa matawi ya kawaida pale ukaguzi unatosha:
dict.get()badala yatry/except KeyErrorkatika kesi rahisi; - kwa rasilimali tumia
with— yenyewe itafunga faili hata kukiwa na kosa.
Hitimisho
Exceptions katika Python ni utaratibu wa kawaida wa kuripoti makosa, si sababu ya kuyaficha. Soma traceback kutoka chini kwenda juu, kamata aina mahususi kwa try/except, tumia else kwa msimbo wa "kila kitu kimeenda sawa" na finally kwa usafishaji. Python raise inakuwezesha kuripoti kosa mwenyewe, raise from na add_note zinahifadhi muktadha, na exceptions zako zinarithi kutoka Exception. Katika kozi ya Python mada hii inafundishwa kwa vitendo: kifuatiliaji cha matumizi kinaacha kuanguka kwa sababu ya mstari mmoja mbaya wa CSV, na kinaeleza kuzidi kwa bajeti kwa exception yake mwenyewe.
Maswali ya mara kwa mara
Kosa linatofautianaje na exception katika Python?
Kwa lugha ya kawaida ni kitu kimoja. Kiufundi, SyntaxError hugunduliwa tayari wakati wa kuchambua msimbo, huku makosa mengine yanatokea wakati wa utekelezaji kama vitu vya exception vinavyoweza kukamatwa.
Je, naweza kuendelea na utekelezaji kutoka mahali kosa lilipotokea?
Hapana. Baada ya kukamata, utekelezaji unaendelea baada ya kizuizi cha try/except, si kutoka mstari uliosababisha kosa. Ikiwa unataka kushughulikia kila mstari wa faili, weka try ndani ya mzunguko.
Itakuwaje kosa likitokea ndani ya except?
Litapanda zaidi, na exception ya asili itahifadhiwa kwenye sifa yake __context__. Kwenye traceback itaonekana maneno During handling of the above exception, another exception occurred.
Ninawezaje kukagua kwenye majaribio kwamba kazi inatupa exception?
Kwenye pytest tumia with pytest.raises(ValueError):, na kwenye unittest — self.assertRaises(ValueError). Jaribio litapita tu ikiwa ndani ya kizuizi kumetokea exception ya aina sahihi.
assert ni nini na inatofautianaje na raise?
assert sharti, "ujumbe" hutupa AssertionError sharti likiwa si kweli. Ni zana ya ukaguzi wakati wa maendeleo: ikiendeshwa kwa bendera -O, assert zinazimwa, kwa hiyo ingizo la mtumiaji linapaswa kukaguliwa kwa if na raise.
Mada hii inafundishwa kwa kina pamoja na mazoezi katika kozi “Python 3.15: от новичка до профи”.
Nenda kwenye kozi