Asosiy qismga o'tish

Xatolar bilan ishlash: try / except

20-bob 16 kod bloki 1774 so'z

Bu bobda

  • traceback ni o'qishni
  • try / except bilan xatoni ushlab, dasturni yiqilishdan saqlashni
  • else va finally qachon kerakligini
  • bir nechta xato turini alohida ushlashni
  • o'z funksiyangizda raise bilan xato berishni

Xato — bu xabar

Shu paytgacha xato chiqsa, dastur to'xtardi. Python xatolarni istisno (exception) deb ataydi va ular haqida batafsil xabar beradi. Jupyterda u shunday ko'rinadi:

python7 qator
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[3], line 2
      1 a = 10
----> 2 print(a / 0)

ZeroDivisionError: division by zero

Bu traceback. Uni pastdan yuqoriga o'qing:

  1. Oxirgi qator — xato turi va sababi: ZeroDivisionError: division by zero.
  2. Strelka ----> — qaysi qatorda yuz berdi: 2-qator.
  3. Funksiyalar ichida bo'lsa, yuqorida chaqiruvlar zanjiri ko'rinadi.

Kitob davomida uchratgan xatolarimiz:

Xato Qachon Bob
SyntaxError yozuv qoidasi buzilgan (: yo'q, = o'rniga ==) 1, 6
IndentationError chekinish noto'g'ri 6
NameError bunday nom yo'q 1
TypeError tur mos emas: "20" + 1 2
ValueError qiymat noto'g'ri: int("abc") 2
ZeroDivisionError nolga bo'lish 3
IndexError indeks chegaradan tashqari 5, 9
KeyError lug'atda bunday kalit yo'q 11
AttributeError obyektda bunday atribut yo'q 17
FileNotFoundError fayl topilmadi 19

Ba'zi xatolar — dasturchi xatosi (SyntaxError, NameError): ularni kodni tuzatib yo'qotasiz. Boshqalari — tashqi dunyo xatosi: foydalanuvchi harf kiritdi, fayl yo'q, internet uzildi. Bularni oldindan bilib bo'lmaydi — ularni ushlash kerak.

try / except

python5 qator
try:
    print(5 / 0)
except ZeroDivisionError:
    print("Nolga bo'lib bo'lmaydi!")
print("Dastur davom etmoqda")
Natija
Nolga bo'lib bo'lmaydi!
Dastur davom etmoqda
  • try: ichidagi kod sinab ko'riladi.
  • Xato chiqsa, Python try ning qolgan qismini tashlab, mos except ga o'tadi.
  • Xato ushlangani uchun dastur yiqilmadi va davom etdi.

Foydalanuvchi kiritishini himoyalash

Bo'lish kalkulyatori. Birinchi variant int() dagi xatoni hisobga olmaydi:

python14 qator
print("Ikki son kiriting, bo'lib beraman. Chiqish uchun 'q'.")
while True:
    birinchi = input("Birinchi son: ")
    if birinchi == "q":
        break
    ikkinchi = input("Ikkinchi son: ")
    if ikkinchi == "q":
        break
    try:
        javob = int(birinchi) / int(ikkinchi)
    except ZeroDivisionError:
        print("Nolga bo'lib bo'lmaydi!")
    else:
        print(javob)
Natija
Ikki son kiriting, bo'lib beraman. Chiqish uchun 'q'.
Birinchi son: 12
Ikkinchi son: 0
Nolga bo'lib bo'lmaydi!
Birinchi son: 8
Ikkinchi son: abc
Xato xabari
ValueError: invalid literal for int() with base 10: 'abc'

Nolni ushladik, lekin abc kiritilganda int() ValueError berdi va dastur yiqildi. Ikkala xatoni ham ushlaymiz:

python16 qator
print("Ikki son kiriting, bo'lib beraman. Chiqish uchun 'q'.")
while True:
    birinchi = input("Birinchi son: ")
    if birinchi == "q":
        break
    ikkinchi = input("Ikkinchi son: ")
    if ikkinchi == "q":
        break
    try:
        javob = int(birinchi) / int(ikkinchi)
    except ZeroDivisionError:
        print("Nolga bo'lib bo'lmaydi!")
    except ValueError:
        print("Faqat butun son kiriting!")
    else:
        print(javob)
Natija
Ikki son kiriting, bo'lib beraman. Chiqish uchun 'q'.
Birinchi son: 12
Ikkinchi son: 0
Nolga bo'lib bo'lmaydi!
Birinchi son: 8
Ikkinchi son: abc
Faqat butun son kiriting!
Birinchi son: 9
Ikkinchi son: 3
3.0
Birinchi son: q

Har bir except o'z xato turini ushlaydi. Endi dasturni hech qanday kiritish bilan yiqitib bo'lmaydi.

else va finally

To'liq tuzilma:

yaml8 qator
try:
    # xato berishi mumkin bo'lgan kod
except XatoTuri:
    # xato bo'lganda
else:
    # xato BO'LMAGANDA
finally:
    # har doim, xato bo'lsa ham, bo'lmasa ham
  • else — faqat try muvaffaqiyatli o'tganda. Xato bermaydigan kodni try ichiga tiqmaslik uchun ishlatiladi: try imkon qadar qisqa bo'lsin.
  • finally — tozalash uchun: faylni yopish, ulanishni uzish, "tugadi" deb yozish.
python15 qator
def bol(a, b):
    try:
        natija = a / b
    except ZeroDivisionError:
        print(f"{a} / {b}: nolga bo'lish")
        return None
    else:
        print(f"{a} / {b} = {natija}")
        return natija
    finally:
        print("  (bol() tugadi)")


bol(10, 4)
bol(1, 0)
Natija
10 / 4 = 2.5
  (bol() tugadi)
1 / 0: nolga bo'lish
  (bol() tugadi)

finally hatto return dan keyin ham bajarildi.

Xato haqida ma'lumot olish: as e

python6 qator
qiymatlar = ["12", "3.5", "abc", ""]
for q in qiymatlar:
    try:
        print(int(q))
    except ValueError as e:
        print(f"{q!r} -> xato: {e}")
Natija
12
'3.5' -> xato: invalid literal for int() with base 10: '3.5'
'abc' -> xato: invalid literal for int() with base 10: 'abc'
'' -> xato: invalid literal for int() with base 10: ''

as e — xato obyektini e ga bog'laydi; str(e) — Python bergan xabar.

Fayl topilmasa

python8 qator
fayl = "alisa.txt"
try:
    with open(fayl, encoding="utf-8") as f:
        matn = f.read()
except FileNotFoundError:
    print(f"Kechirasiz, {fayl} fayli topilmadi.")
else:
    print(len(matn.split()), "so'z")
Natija
Kechirasiz, alisa.txt fayli topilmadi.

Bir nechta faylni ko'rib chiqishda — yo'g'ini o'tkazib yuborib davom etish:

python12 qator
from pathlib import Path

Path("bir.txt").write_text("olma anor behi", encoding="utf-8")
Path("uch.txt").write_text("bir ikki", encoding="utf-8")

for nom in ["bir.txt", "ikki.txt", "uch.txt"]:
    try:
        with open(nom, encoding="utf-8") as f:
            soni = len(f.read().split())
    except FileNotFoundError:
        continue
    print(f"{nom}: {soni} so'z")
Natija
bir.txt: 3 so'z
uch.txt: 2 so'z

Xato haqida xabar ham kerak bo'lmasa, except ichiga pass yoziladi ("hech narsa qilma"). Lekin buni ehtiyot bilan ishlating: jimgina yutilgan xato keyin topilishi eng qiyin xatoga aylanadi.

Hamma xatoni ko'r-ko'rona ushlash

python8 qator
def ortacha(royxat):
    try:
        return sum(royxat) / len(royxt)
    except Exception:
        return 0


print(ortacha([4, 5, 5]))
Natija
0

Javob 0 — noto'g'ri, lekin xato ham yo'q! len(royxt) dagi yozuv xatosi (NameError) except Exception ga tushib, yashirinib qoldi. Qoida: faqat kutgan xatoingizni ushlang — except ZeroDivisionError:, except ValueError:. except: yoki except Exception: faqat o'ylab, xabarni chiqarib yoki yozib qo'yib ishlatiladi.

raise: o'zingiz xato berish

O'z funksiyangiz noto'g'ri qiymat olsa, jim natija qaytarishdan ko'ra xato berish yaxshiroq. 18-bobda @property setter ida shuni qildik:

python13 qator
def yosh_tekshir(yosh):
    if not isinstance(yosh, int):
        raise TypeError("Yosh butun son bo'lishi kerak")
    if not 0 < yosh < 120:
        raise ValueError(f"Yosh 1 dan 119 gacha bo'lishi kerak, berildi: {yosh}")
    return yosh


for qiymat in [25, -3, "20"]:
    try:
        print("To'g'ri:", yosh_tekshir(qiymat))
    except (TypeError, ValueError) as e:
        print(f"{type(e).__name__}: {e}")
Natija
To'g'ri: 25
ValueError: Yosh 1 dan 119 gacha bo'lishi kerak, berildi: -3
TypeError: Yosh butun son bo'lishi kerak

except (TypeError, ValueError) — kortej bilan bir nechta turni bitta except da ushlash.

Loyiha: to'g'ri son olguncha so'rash

8-bobda isdigit() bilan qilgan edik, lekin u manfiy va kasr sonlarni tanimaydi. try bilan hammasi to'g'ri ishlaydi:

python11 qator
def son_sora(savol):
    while True:
        matn = input(savol)
        try:
            return float(matn)
        except ValueError:
            print(f"'{matn}' — son emas, qaytadan kiriting.")


harorat = son_sora("Harorat: ")
print("Qabul qilindi:", harorat)
Natija
Harorat: o'n
'o'n' — son emas, qaytadan kiriting.
Harorat: -7.5
Qabul qilindi: -7.5

Bu funksiyani endi istalgan dasturda ishlatish mumkin — 22-bobdagi loyihada aynan shunday qilamiz.

Kitoblarda

  • Python asoslari: istisnolar bilan ishlash 92–93-betlar.
  • Python Crash Course: 10-bob, Exceptions 194–202-betlar (ZeroDivisionError, Using try-except Blocks, The else Block, Handling the FileNotFoundError Exception, Failing Silently).

Xulosa

  • Traceback ni pastdan o'qing: oxirgi qator — xato turi va sababi.
  • try: → except Tur: → else: (xato bo'lmasa) → finally: (har doim).
  • Faqat kutgan xatoingizni ushlang; except Exception: haqiqiy xatolarni yashiradi.
  • except ValueError as e: — xabarni olish; except (A, B): — bir nechta tur.
  • raise ValueError("...") — o'z funksiyangizda noto'g'ri qiymatni rad etish.

Mashqlar

20.1

Ikki qatorda ikki qiymat berilgan. Birinchisini ikkinchisiga bo'lib natijani chiqaring. Nolga bo'lishda — Nolga bo'lib bo'lmaydi, son bo'lmasa — Son kiriting.

KirishKutilgan natija
10, 42.5
7, 0Nolga bo'lib bo'lmaydi
7, abcSon kiriting
Yechimni ko'rish
python6 qator
try:
    print(float(input()) / float(input()))
except ZeroDivisionError:
    print("Nolga bo'lib bo'lmaydi")
except ValueError:
    print("Son kiriting")
20.2

Bitta qatorda qiymatlar berilgan. Ulardan son bo'lganlarining yig'indisini chiqaring (kasr ham bo'lishi mumkin); son bo'lmaganlarini o'tkazib yuboring.

KirishKutilgan natija
5 abc 2.5 -1 x6.5
olma anor0
1010
Yechimni ko'rish
python7 qator
s = 0
for q in input().split():
    try:
        s += float(q)
    except ValueError:
        pass
print(s)
20.3

Ro'yxat [10, 20, 30, 40, 50] va indeks berilgan. Shu indeksdagi elementni chiqaring. Indeks chegaradan tashqari bo'lsa — Bunday indeks yo'q, son bo'lmasa — Indeks son bo'lishi kerak.

KirishKutilgan natija
230
-150
9Bunday indeks yo'q
Yechimni ko'rish
python7 qator
r = [10, 20, 30, 40, 50]
try:
    print(r[int(input())])
except IndexError:
    print("Bunday indeks yo'q")
except ValueError:
    print("Indeks son bo'lishi kerak")
20.4

kvadrat_ildiz(x) funksiyasini yozing: x manfiy bo'lsa ValueError("Manfiy sondan ildiz yo'q") beradi, aks holda x ** 0.5 qaytaradi. Son berilgan; natijani yoki xato xabarini chiqaring.

KirishKutilgan natija
164
21.4142135623730951
-9Manfiy sondan ildiz yo'q
Yechimni ko'rish
python10 qator
def kvadrat_ildiz(x):
    if x < 0:
        raise ValueError("Manfiy sondan ildiz yo'q")
    return x ** 0.5


try:
    print(kvadrat_ildiz(float(input())))
except ValueError as e:
    print(e)

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar