10. Xatolarni boshqarish

Dastur ishlayotganda turli xil xatolar yuz berishi mumkin.

Masalan:

son = int(
    input("Son kiriting: ")
)

Foydalanuvchi:

salom

kiritsa, dastur xatolik bilan to‘xtaydi.

Bunday vaziyatlarni boshqarish uchun Python'da istisnolarni boshqarish mexanizmi mavjud.

Asosiy konstruksiyalar:

try
except
else
finally
raise

Xatolik nima?

Xatolik dastur bajarilishi vaqtida kutilmagan holat yuz berganda paydo bo‘ladi.

Masalan:

birinchi_son = 10
ikkinchi_son = 0

natija = (
    birinchi_son
    / ikkinchi_son
)

print(
    natija
)

Bu kod:

ZeroDivisionError

xatoligini beradi.

Chunki sonni nolga bo‘lish mumkin emas.


SyntaxError

Kod sintaksisi noto‘g‘ri bo‘lsa, SyntaxError yuz beradi.

Masalan:

if True
    print("Salom")

Bu kodda : yetishmaydi.

Python kodni bajarishdan oldin sintaksis xatosini aniqlaydi.

To‘g‘ri variant:

if True:

    print(
        "Salom, Softromeda!"
    )

NameError

Mavjud bo‘lmagan o‘zgaruvchidan foydalanilganda NameError yuz beradi.

print(
    foydalanuvchi_nomi
)

Agar foydalanuvchi_nomi oldindan yaratilmagan bo‘lsa:

NameError

yuz beradi.


TypeError

Noto‘g‘ri turdagi qiymatlar bilan amal bajarilganda TypeError yuz berishi mumkin.

birinchi_son = 10
ikkinchi_son = "20"

natija = (
    birinchi_son
    + ikkinchi_son
)

Bu yerda int va str qiymatlarni to‘g‘ridan-to‘g‘ri qo‘shib bo‘lmaydi.


ValueError

Qiymat turi to‘g‘ri bo‘lsa ham, qiymatning o‘zi yaroqsiz bo‘lsa ValueError yuz beradi.

yosh = int(
    "yigirma"
)

"yigirma" matnini intga aylantirib bo‘lmaydi.


IndexError

Listdagi mavjud bo‘lmagan indeksga murojaat qilinsa:

mevalar = [
    "olma",
    "banan"
]

print(
    mevalar[5]
)

IndexError yuz beradi.


KeyError

Dictionaryda mavjud bo‘lmagan kalitga murojaat qilinsa:

foydalanuvchi = {
    "ism": "Softromeda"
}

print(
    foydalanuvchi["yosh"]
)

KeyError yuz beradi.


AttributeError

Obyektda mavjud bo‘lmagan atribut yoki metodga murojaat qilinsa:

matn = "Softromeda"

matn.mavjud_emas()

AttributeError yuz beradi.


try

Xatolik yuz berishi mumkin bo‘lgan kod try ichiga yoziladi.

try:

    son = int(
        input("Son kiriting: ")
    )

    print(
        f"Siz {son} sonini kiritdingiz."
    )

except:

    print(
        "Xatolik yuz berdi."
    )

Agar foydalanuvchi:

25

kiritsa:

Siz 25 sonini kiritdingiz.

Agar:

salom

kiritsa:

Xatolik yuz berdi.

Muayyan xatoni ushlash

Bo‘sh except ishlatishdan ko‘ra, qaysi xatoni boshqarayotganimizni ko‘rsatish yaxshiroq.

try:

    son = int(
        input("Son kiriting: ")
    )

except ValueError:

    print(
        "Iltimos, faqat son kiriting."
    )

Bu yerda faqat ValueError boshqariladi.


Bir nechta except

Bir nechta turdagi xatolarni alohida boshqarish mumkin.

try:

    birinchi_son = int(
        input("Birinchi son: ")
    )

    ikkinchi_son = int(
        input("Ikkinchi son: ")
    )

    natija = (
        birinchi_son
        / ikkinchi_son
    )

    print(
        f"Natija: {natija}"
    )

except ValueError:

    print(
        "Iltimos, son kiriting."
    )

except ZeroDivisionError:

    print(
        "Sonni nolga bo‘lish mumkin emas."
    )

except ichida xatoni olish

Xatolik obyektini o‘zgaruvchiga saqlash mumkin.

try:

    son = int(
        "salom"
    )

except ValueError as xatolik:

    print(
        f"Xatolik: {xatolik}"
    )

else

try muvaffaqiyatli ishlasa, else bajariladi.

try:

    son = int(
        input("Son kiriting: ")
    )

except ValueError:

    print(
        "Noto‘g‘ri son."
    )

else:

    print(
        f"Siz {son} sonini kiritdingiz."
    )

Tuzilishi:

try
 ↓
Xatolik bormi?
 ↓
Ha → except
 ↓
Yo‘q → else

finally

finally ichidagi kod xatolik bo‘lishidan qat’i nazar bajariladi.

try:

    son = int(
        input("Son kiriting: ")
    )

except ValueError:

    print(
        "Noto‘g‘ri qiymat."
    )

finally:

    print(
        "Dastur yakunlandi."
    )

try-except-else-finally

Barcha qismlarni birga ishlatish mumkin:

try:

    birinchi_son = int(
        input("Birinchi son: ")
    )

    ikkinchi_son = int(
        input("Ikkinchi son: ")
    )

    natija = (
        birinchi_son
        / ikkinchi_son
    )

except ValueError:

    print(
        "Sonlarni to‘g‘ri kiriting."
    )

except ZeroDivisionError:

    print(
        "Ikkinchi son nol bo‘lishi mumkin emas."
    )

else:

    print(
        f"Natija: {natija}"
    )

finally:

    print(
        "Hisoblash yakunlandi."
    )

raise

Dasturchi o‘zi xatolik chiqarishi uchun raise ishlatiladi.

yosh = -5

if yosh < 0:

    raise ValueError(
        "Yosh manfiy bo‘lishi mumkin emas."
    )

Funksiyada raise

def yoshni_tekshir(
    yosh
):

    if yosh < 0:

        raise ValueError(
            "Yosh manfiy bo‘lishi mumkin emas."
        )

    return yosh

Foydalanish:

try:

    yosh = yoshni_tekshir(
        -5
    )

except ValueError as xatolik:

    print(
        f"Xatolik: {xatolik}"
    )

Foydalanuvchi ma’lumotini tekshirish

raise foydalanuvchi ma’lumotlarini tekshirishda juda foydali.

def yoshni_tekshir(
    yosh
):

    if yosh < 0:

        raise ValueError(
            "Yosh 0 dan kichik bo‘lishi mumkin emas."
        )

    if yosh > 150:

        raise ValueError(
            "Yosh juda katta."
        )

    return yosh

Keyin:

try:

    yosh = int(
        input("Yoshingizni kiriting: ")
    )

    yoshni_tekshir(
        yosh
    )

    print(
        "Yosh qabul qilindi."
    )

except ValueError as xatolik:

    print(
        f"Xatolik: {xatolik}"
    )

assert

assert dasturdagi shartni tekshirish uchun ishlatiladi.

yosh = 25

assert yosh >= 0

Agar shart noto‘g‘ri bo‘lsa:

AssertionError

yuz beradi.

Xabar bilan:

yosh = -5

assert yosh >= 0, (
    "Yosh manfiy bo‘lishi mumkin emas."
)

assert va raise farqi

assert ko‘proq dasturchining ichki taxminlarini tekshirish uchun ishlatiladi.

assert foydalanuvchi is not None

raise esa dastur logikasida kutilgan noto‘g‘ri holatni aniq boshqarish uchun ishlatiladi.

if yosh < 0:

    raise ValueError(
        "Yosh noto‘g‘ri."
    )

O‘zimizning exception classimiz

Python'da o‘zimizga kerakli xatolik turini yaratishimiz mumkin.

class YoshXatoligi(Exception):

    pass

Keyin:

def yoshni_tekshir(
    yosh
):

    if yosh < 0:

        raise YoshXatoligi(
            "Yosh manfiy bo‘lishi mumkin emas."
        )

Foydalanish:

try:

    yoshni_tekshir(
        -5
    )

except YoshXatoligi as xatolik:

    print(
        f"Xatolik: {xatolik}"
    )

Exception meros olish

Maxsus xatoliklar odatda Exceptiondan meros oladi.

class MalumotXatoligi(
    Exception
):

    pass

Keyin:

class YoshXatoligi(
    MalumotXatoligi
):

    pass

Bu katta dasturlarda xatoliklarni guruhlash uchun foydali.


Xatoliklarni qayta ko‘tarish

Ba’zan xatoni ushlab, keyin yana yuqoriga yuborish kerak bo‘ladi.

try:

    son = int(
        "salom"
    )

except ValueError:

    print(
        "Qiymatni aylantirishda xatolik."
    )

    raise

raise argumentlarsiz yozilsa, ushlangan xatolikni qayta chiqaradi.


Xatoliklar iyerarxiyasi

Python'dagi ko‘plab xatoliklar umumiy Exception sinfidan kelib chiqadi.

Masalan:

Exception
│
├── ValueError
├── TypeError
├── IndexError
├── KeyError
├── NameError
└── ZeroDivisionError

Shuning uchun quyidagisi ko‘plab xatoliklarni ushlashi mumkin:

try:

    natija = int(
        "salom"
    )

except Exception as xatolik:

    print(
        f"Xatolik: {xatolik}"
    )

Lekin imkon qadar aniq exception turini ushlash yaxshi amaliyot hisoblanadi.


Bir nechta xatolikni bitta exceptda boshqarish

try:

    son = int(
        input("Son kiriting: ")
    )

except (
    ValueError,
    TypeError
):

    print(
        "Qiymat noto‘g‘ri."
    )

Xatolik bilan birga else

try:

    son = int(
        input("Son kiriting: ")
    )

except ValueError:

    print(
        "Son noto‘g‘ri."
    )

else:

    kvadrat = (
        son ** 2
    )

    print(
        f"Kvadrat: {kvadrat}"
    )

Bu yerda hisoblash faqat son muvaffaqiyatli o‘qilganida amalga oshadi.


finally qachon foydali?

finally resurslarni tozalashda ayniqsa foydali.

Masalan:

fayl = None

try:

    fayl = open(
        "malumot.txt",
        "r"
    )

    matn = fayl.read()

    print(
        matn
    )

except OSError:

    print(
        "Faylni ochishda xatolik."
    )

finally:

    if fayl is not None:

        fayl.close()

    print(
        "Fayl yopildi."
    )

Keyinchalik fayllar bilan ishlashda with konstruksiyasi bundan ham qulayroq usul ekanini ko‘ramiz.


Noto‘g‘ri usul

Quyidagicha juda umumiy exception ushlash muammolarni yashirishi mumkin:

try:

    juda_muhim_amallar()

except:

    pass

Bu yomon amaliyot.

Chunki dasturda xatolik yuz beradi, lekin hech qanday ma’lumot ko‘rsatilmaydi.

Natijada muammoni topish qiyinlashadi.

Yaxshiroq:

try:

    juda_muhim_amallar()

except ValueError as xatolik:

    print(
        f"Qiymat xatoligi: {xatolik}"
    )

Xatolikni foydalanuvchiga tushunarli qilish

Texnik xatolikni to‘g‘ridan-to‘g‘ri foydalanuvchiga ko‘rsatish har doim ham yaxshi emas.

Masalan:

try:

    yosh = int(
        input("Yoshingizni kiriting: ")
    )

except ValueError:

    print(
        "Iltimos, yoshingizni raqam bilan kiriting."
    )

Bu foydalanuvchi uchun ancha tushunarli.


Qayta urinish

Xatolik yuz berganda foydalanuvchidan yana qiymat olish mumkin.

while True:

    try:

        yosh = int(
            input("Yoshingizni kiriting: ")
        )

        break

    except ValueError:

        print(
            "Iltimos, faqat son kiriting."
        )

print(
    f"Sizning yoshingiz: {yosh}"
)

Agar foydalanuvchi noto‘g‘ri qiymat kiritsa, dastur yana so‘raydi.


Cheklangan urinishlar

Foydalanuvchiga uchta imkoniyat berish:

urinishlar_soni = 3

while urinishlar_soni > 0:

    try:

        yosh = int(
            input("Yoshingizni kiriting: ")
        )

        if yosh < 0:

            raise ValueError(
                "Yosh manfiy bo‘lishi mumkin emas."
            )

        print(
            f"Qabul qilindi: {yosh}"
        )

        break

    except ValueError as xatolik:

        urinishlar_soni -= 1

        print(
            f"Xatolik: {xatolik}"
        )

        print(
            f"Qolgan urinishlar: {urinishlar_soni}"
        )

Funksiya va exception

Funksiya ichida exception yuz berishi mumkin.

def bolish(
    birinchi_son,
    ikkinchi_son
):

    if ikkinchi_son == 0:

        raise ZeroDivisionError(
            "Nolga bo‘lish mumkin emas."
        )

    return (
        birinchi_son
        / ikkinchi_son
    )

Keyin:

try:

    natija = bolish(
        10,
        0
    )

except ZeroDivisionError as xatolik:

    print(
        f"Xatolik: {xatolik}"
    )

Exception va return

Xatolikni qaytarish uchun returndan foydalanish mumkin, lekin bu har doim ham yaxshi yechim emas.

Masalan:

def bolish(
    birinchi_son,
    ikkinchi_son
):

    if ikkinchi_son == 0:

        return None

    return (
        birinchi_son
        / ikkinchi_son
    )

Bu oddiy vaziyatlarda ishlashi mumkin.

Lekin murakkab dasturlarda xatolikni raise qilish ko‘pincha aniqroq:

def bolish(
    birinchi_son,
    ikkinchi_son
):

    if ikkinchi_son == 0:

        raise ValueError(
            "Ikkinchi son nol bo‘lishi mumkin emas."
        )

    return (
        birinchi_son
        / ikkinchi_son
    )

Amaliy mashqlar

Mashq 1

Foydalanuvchidan ikkita son qabul qiling va ularni bo‘ling.

Quyidagi xatoliklarni boshqaring:

ValueError
ZeroDivisionError

Mashq 2

Foydalanuvchidan indeks qabul qilib, list elementini chiqaring.

IndexErrorni boshqaring.

Mashq 3

Dictionarydan foydalanuvchi kiritgan kalit bo‘yicha qiymat oling.

KeyErrorni boshqaring.

Mashq 4

Manfiy yosh uchun ValueError ko‘taring.

Mashq 5

YoshXatoligi nomli maxsus exception yarating.

Mashq 6

Foydalanuvchiga faqat uch marta noto‘g‘ri qiymat kiritish imkonini bering.

Mashq 7

try, except, else va finallyning barchasidan foydalanadigan dastur yozing.

Mashq 8

Bir nechta exceptionni bitta except orqali boshqaring.


Mini loyiha — Softromeda kalkulyator

Foydalanuvchidan ikkita son va amal qabul qilamiz.

def hisobla(
    birinchi_son,
    ikkinchi_son,
    amal
):

    if amal == "+":

        return (
            birinchi_son
            + ikkinchi_son
        )

    if amal == "-":

        return (
            birinchi_son
            - ikkinchi_son
        )

    if amal == "*":

        return (
            birinchi_son
            * ikkinchi_son
        )

    if amal == "/":

        if ikkinchi_son == 0:

            raise ZeroDivisionError(
                "Nolga bo‘lish mumkin emas."
            )

        return (
            birinchi_son
            / ikkinchi_son
        )

    raise ValueError(
        "Noma’lum amal."
    )


try:

    birinchi_son = float(
        input("Birinchi sonni kiriting: ")
    )

    ikkinchi_son = float(
        input("Ikkinchi sonni kiriting: ")
    )

    amal = input(
        "Amalni kiriting (+, -, *, /): "
    )

    natija = hisobla(
        birinchi_son,
        ikkinchi_son,
        amal
    )

except ValueError as xatolik:

    print(
        f"Qiymat xatoligi: {xatolik}"
    )

except ZeroDivisionError as xatolik:

    print(
        f"Hisoblash xatoligi: {xatolik}"
    )

else:

    print(
        f"Natija: {natija}"
    )

finally:

    print(
        "Softromeda kalkulyatori yakunlandi."
    )

Masalan:

Birinchi sonni kiriting: 20
Ikkinchi sonni kiriting: 5
Amalni kiriting (+, -, *, /): /
Natija: 4.0
Softromeda kalkulyatori yakunlandi.

Agar ikkinchi son 0 bo‘lsa:

Hisoblash xatoligi: Nolga bo‘lish mumkin emas.
Softromeda kalkulyatori yakunlandi.

Bob xulosasi

Bu bobda Python'da xatolarni boshqarishni o‘rgandik.

Asosiy tushunchalar:

SyntaxError
NameError
TypeError
ValueError
IndexError
KeyError
AttributeError
ZeroDivisionError

Xatolarni boshqarish:

try
except
else
finally

Xatolik yaratish:

raise

Shartni tekshirish:

assert

Maxsus xatolik yaratish:

class YoshXatoligi(Exception):
    pass

Eng muhim tamoyil:

Xatolik yuz berishi mumkin
        ↓
try
        ↓
Xatolik yuz berdimi?
   ↓              ↓
  ha              yo‘q
   ↓               ↓
except           else
   ↓               ↓
        finally

Keyingi bobda Python'da fayllar bilan ishlash, fayl o‘qish va yozish, with, CSV va JSON formatlarini o‘rganamiz.