IlmHamroh
Python kursi/Xatolar6/8-dars40 daqiqa
Mundarija (21)

9.6-dars: O'z istisnolaringiz

9-QISM — XATOLAR VA ISTISNOLAR · 6-dars


1. Kirish va motivatsiya

Bu kod ishlaydi — lekin foydalanuvchi hech narsa tushunmaydi:

python
def tolov_qil(karta: str, summa: int):
    if len(karta) != 16:
        raise ValueError("noto'g'ri")
    if summa <= 0:
        raise ValueError("noto'g'ri")
    if summa > balans:
        raise ValueError("noto'g'ri")

Uchtasi ham ValueError — ajratib bo'lmaydi:

python
try:
    tolov_qil(k, s)
except ValueError:
    ...                             # ⚠️ qaysi biri? Nima qilish kerak?

O'z istisnolaringiz bilan:

python
class TolovXatosi(Exception): ...
class KartaXatosi(TolovXatosi): ...
class MablagYetarliEmas(TolovXatosi):
    def __init__(self, kerak: int, bor: int):
        self.kerak, self.bor, self.yetishmayapti = kerak, bor, kerak - bor
        super().__init__(f"{self.yetishmayapti:,} so'm yetishmayapti")
python
try:
    tolov_qil(k, s)
except KartaXatosi:
    return {"xato": "Karta ma'lumotlari noto'g'ri"}, 400
except MablagYetarliEmas as e:
    return {"xato": str(e), "yetishmayapti": e.yetishmayapti}, 402

Bu darsda:

  • Qachon o'z istisnongiz kerak
  • Asos sinf tanlash (Exception, ValueError, ...)
  • Atributlar va konstruktor
  • __str__, __repr__ va serializatsiya
  • Keng tarqalgan xatolar
  • Kutubxona uchun istisno loyihalash

2. Nazariya — chuqur tushuntirish

2.1. Eng oddiy shakl

python
class MeningXatom(Exception):
    """Nima uchun kerakligi shu yerda."""

Uch qator — va u to'liq ishlaydi:

python
raise MeningXatom("xabar")
except MeningXatom as e: ...
str(e)                              # 'xabar'
e.args                              # ('xabar',)

pass o'rniga docstring:

python
class MeningXatom(Exception):
    pass                            # ⚠️ hujjat yo'q

class MeningXatom(Exception):
    """Sozlama yuklashda xato."""   # ✅

2.2. Qachon kerak

Kerak:

python
# 1. Domen xatosi — o'rnatilgan tur mos kelmaydi
class MablagYetarliEmas(Exception): ...

# 2. Ajratish kerak
class VaqtinchalikXato(Exception): ...      # qayta urin
class DoimiyXato(Exception): ...            # urinma

# 3. Qo'shimcha ma'lumot
class SorovXatosi(Exception):
    def __init__(self, xabar, *, kod, url): ...

# 4. Kutubxona API si
class KutubxonaXatosi(Exception): ...       # ⭐ foydalanuvchi bitta tur

# 5. Ichki tafsilotni yashirish
raise MeningXatom(...) from None

Kerak emas:

python
# 1. O'rnatilgan tur to'liq mos
class MeningTypeError(Exception): ...       # ⚠️ TypeError yetadi

# 2. Bitta joyda ishlatiladi
class FaqatShuYerda(Exception): ...         # ⚠️ ValueError yetadi

# 3. Ajratilmaydi
except (XatoA, XatoB, XatoC):               # ⚠️ nega ajratilgan?

# 4. Har funksiya uchun
class YuklashXatosi(Exception): ...
class TekshirishXatosi(Exception): ...      # ⚠️ juda ko'p
class SaqlashXatosi(Exception): ...

Mezon: foydalanuvchi bu turni alohida ushlaydimi?

2.3. Asos sinf tanlash

python
class MeningXatom(Exception): ...           # ⭐ sukut tanlov

Qachon boshqasi:

Asos Qachon Misol
Exception Odatda Domen xatolari
ValueError Qiymat yaroqsiz ValidatsiyaXatosi
TypeError Tur mos emas Kamdan-kam
KeyError Kalit yo'q SozlamaTopilmadi
LookupError Qidiruv Konteyner
OSError Tizim resursi Fayl, tarmoq
RuntimeError Boshqa ish vaqti Umumiy
BaseException Hech qachon —

O'rnatilgan turdan meros — ehtiyot:

python
class SozlamaTopilmadi(KeyError):
    """⚠️ str(e) tirnoq bilan chiqadi!"""

e = SozlamaTopilmadi("port")
str(e)                              # "'port'"  ⚠️ tirnoq qo'shildi

KeyError.__str__ — repr(args[0]) qaytaradi.

Yechim:

python
class SozlamaTopilmadi(KeyError):
    def __str__(self):
        return self.args[0]         # ⭐ tirnoqsiz

Ko'p meros ham mumkin:

python
class SozlamaXatosi(ValueError, KeyError):      # ⚠️ ehtiyot
    ...

2.4. Atributlar va konstruktor

python
class MablagYetarliEmas(Exception):
    def __init__(self, kerak: int, bor: int, valyuta: str = "UZS"):
        self.kerak = kerak
        self.bor = bor
        self.valyuta = valyuta
        self.yetishmayapti = kerak - bor
        super().__init__(                       # ⭐ SHART
            f"{self.yetishmayapti:,} {valyuta} yetishmayapti"
        )

super().__init__() unutish:

python
class Yomon(Exception):
    def __init__(self, x):
        self.x = x                              # ⚠️ super() yo'q

e = Yomon(5)
str(e)                                          # '5'   — hozircha "ishlaydi"
e.args                                          # (5,)

Nega ishlaydi? BaseException.__new__ konstruktorga berilgan pozitsion argumentlarni __init__ dan oldin args ga yozib qo'yadi. Shuning uchun super().__init__() chaqirilmasa ham args bo'sh qolmaydi. Lekin ikkita muammo bor:

python
class Yomon2(Exception):
    def __init__(self, kerak, bor):
        self.kerak, self.bor = kerak, bor       # ⚠️ super() yo'q

str(Yomon2(1000, 300))                          # '(1000, 300)'  ⚠️ xom kortej

class Yomon3(Exception):
    def __init__(self, *, kod):                 # faqat nomli argument
        self.kod = kod

str(Yomon3(kod=404))                            # ''  ⚠️ BO'SH
Yomon3(kod=404).args                            # ()

Xulosa: super().__init__() chaqirmasangiz, str(e) foydalanuvchi uchun yozilgan xabar bo'lmaydi — u yo xom kortej, yo (nomli argumentlarda) bo'sh qator bo'ladi.

args va pickle:

python
class Yaxshi(Exception):
    def __init__(self, kerak, bor):
        self.kerak, self.bor = kerak, bor
        super().__init__(kerak, bor)            # ⭐ args ga

pickle istisnoni Sinf(*e.args) bilan qayta yaratadi — shuning uchun args konstruktor argumentlariga mos bo'lishi kerak.

Muammo:

python
class Xato(Exception):
    def __init__(self, kerak, bor):
        super().__init__(f"{kerak-bor} yetishmayapti")   # ⚠️ 1 argument

pickle.loads(pickle.dumps(Xato(100, 50)))       # ❌ TypeError

Yechimlar:

python
# 1. args ga barcha argumentlar
super().__init__(kerak, bor)
def __str__(self): return f"{self.kerak - self.bor} yetishmayapti"

# 2. __reduce__ bilan
def __reduce__(self):
    return (type(self), (self.kerak, self.bor))

2.5. __str__ va __repr__

python
class SorovXatosi(Exception):
    def __init__(self, xabar, *, url="", kod=0):
        self.url, self.kod = url, kod
        super().__init__(xabar)

    def __str__(self):
        q = [self.args[0]]
        if self.kod:
            q.append(f"HTTP {self.kod}")
        if self.url:
            q.append(self.url)
        return " | ".join(q)

    def __repr__(self):
        return (f"{type(self).__name__}({self.args[0]!r}, "
                f"url={self.url!r}, kod={self.kod})")

Sukut __repr__:

python
repr(ValueError("a", "b"))          # "ValueError('a', 'b')"

args dan yasaladi.

2.6. Kutubxona uchun loyihalash

python
# ⭐ 1. Bitta asos — foydalanuvchi hammasini ushlashi mumkin
class WisarXatosi(Exception):
    """Barcha wisar xatolarining asosi."""

# ⭐ 2. Toifalar
class SozlamaXatosi(WisarXatosi): ...
class TarmoqXatosi(WisarXatosi): ...
class MalumotXatosi(WisarXatosi): ...

# ⭐ 3. Aniq xatolar
class SozlamaTopilmadi(SozlamaXatosi): ...
class SozlamaBuzilgan(SozlamaXatosi): ...
class UlanmadiXato(TarmoqXatosi): ...
class VaqtTugadi(TarmoqXatosi): ...

Foydalanuvchi tanlaydi:

python
except WisarXatosi:                 # hammasi
except TarmoqXatosi:                # toifa
except VaqtTugadi:                  # aniq

O'rnatilgan turlar bilan moslik:

python
class SozlamaTopilmadi(WisarXatosi, KeyError):
    """⭐ Ikkalasi bilan ham ushlanadi."""

Bu ehtiyotkorlik talab qiladi — MRO va __str__ muammolari.

Modul tuzilishi:

python
# xatolar.py
__all__ = ["WisarXatosi", "SozlamaXatosi", ...]

# __init__.py
from .xatolar import *              # ⭐ ommaviy API

2.7. Keng tarqalgan xatolar

python
# ❌ 1. BaseException dan meros
class MeningXatom(BaseException): ...       # ⚠️ Ctrl+C kabi

# ❌ 2. super().__init__() unutish
def __init__(self, a, b): self.a = a        # ⚠️ str(e) = '(a, b)' xom kortej

# ❌ 3. Nom "Error" bilan tugamaydi
class Muammo(Exception): ...                # ⚠️ PEP 8: XatoError/Error

# ❌ 4. Juda ko'p sinf
class F1Xatosi(Exception): ...              # ⚠️ har funksiya uchun

# ❌ 5. Asos sinf yo'q
class XatoA(Exception): ...                 # ⚠️ bog'lanmagan
class XatoB(Exception): ...

# ❌ 6. Mantiq istisno ichida
class Xato(Exception):
    def __init__(self, m):
        jurnal.error(m)                     # ⚠️ yon ta'sir
        yubor_xabar(m)                      # ⚠️

# ❌ 7. O'zgaruvchan sukut
class Xato(Exception):
    def __init__(self, teglar=[]): ...      # ⚠️

3. Tez ma'lumotnoma

Eng oddiy

python
class MeningXatom(Exception):
    """Nima uchun kerak."""

Atributlar bilan

python
class Xato(Exception):
    def __init__(self, kerak, bor):
        self.kerak, self.bor = kerak, bor
        self.yetishmayapti = kerak - bor
        super().__init__(kerak, bor)        ⭐ args ga (pickle uchun)

    def __str__(self):
        return f"{self.yetishmayapti} yetishmayapti"

Asos sinf

Exception       ⭐ sukut tanlov
ValueError      qiymat yaroqsiz
KeyError        ⚠️ str(e) tirnoq bilan
OSError         tizim resursi
BaseException   ❌ hech qachon

Kutubxona ierarxiyasi

KutubxonaXatosi                 ⭐ bitta asos
 ├── ToifaA
 │    ├── AniqXato1
 │    └── AniqXato2
 └── ToifaB

Xatolar

BaseException dan meros         ❌
super().__init__() unutish      ❌ str(e) xom kortej yoki bo'sh
args konstruktorga mos emas     ⚠️ pickle buziladi
Har funksiya uchun sinf         ⚠️ ortiqcha
Istisno ichida yon ta'sir       ❌

4. Batafsil misollar

Misol 1 — Asoslar va tuzoqlar

python
"""O'z istisnolaringizni to'g'ri yaratish."""

import pickle
import traceback

print("=== 1. Eng oddiy shakl ===\n")


class MeningXatom(Exception):
    """Uch qator — va to'liq ishlaydi."""


try:
    raise MeningXatom("nimadir noto'g'ri")
except MeningXatom as e:
    print(f"  str(e)   = {str(e)!r}")
    print(f"  repr(e)  = {repr(e)}")
    print(f"  e.args   = {e.args}")
    print(f"  MRO      = {[c.__name__ for c in type(e).__mro__]}")

print(f"\n  Bir necha argument:")
try:
    raise MeningXatom("xabar", 42, {"kalit": "qiymat"})
except MeningXatom as e:
    print(f"    str(e)  = {str(e)!r}")
    print(f"    e.args  = {e.args}")

print(f"\n  Argumentsiz:")
try:
    raise MeningXatom
except MeningXatom as e:
    print(f"    str(e)  = {str(e)!r}")
    print(f"    e.args  = {e.args}")


print("\n\n=== ⚠️ 2. super().__init__() unutish ===\n")


class Yomon(Exception):
    def __init__(self, kerak, bor):
        self.kerak = kerak
        self.bor = bor                          # ⚠️ super() YO'Q


class Yaxshi(Exception):
    def __init__(self, kerak, bor):
        self.kerak = kerak
        self.bor = bor
        super().__init__(f"{kerak - bor} yetishmayapti")     # ⭐


class EngYaxshi(Exception):
    def __init__(self, kerak, bor):
        self.kerak = kerak
        self.bor = bor
        self.yetishmayapti = kerak - bor
        super().__init__(kerak, bor)            # ⭐ args ga

    def __str__(self):
        return f"{self.yetishmayapti:,} yetishmayapti"


print(f"  {'Sinf':<14} {'str(e)':<26} {'e.args':<20} {'pickle'}")
print("  " + "─" * 72)
for Sinf in (Yomon, Yaxshi, EngYaxshi):
    e = Sinf(1000, 300)
    try:
        pickle.loads(pickle.dumps(e))
        p = "✅"
    except Exception as ex:
        p = f"❌ {type(ex).__name__}"
    print(f"  {Sinf.__name__:<14} {str(e)!r:<26} {str(e.args):<20} {p}")

print(f"""
  ⚠️ Yomon:    str(e) = '(1000, 300)' — xom kortej, foydalanuvchi
                nima bo'lganini tushunmaydi (BaseException.__new__
                args ni o'zi to'ldirgan, lekin xabar yozilmagan)
  ⚠️ Yaxshi:   pickle BUZILADI — args (1 ta) konstruktorga (2 ta)
                mos emas
  ✅ EngYaxshi: args konstruktor argumentlariga MOS

  ⭐ SABAB: pickle istisnoni Sinf(*e.args) bilan qayta yaratadi.
""")

print(f"  Tekshiruv:")
e = EngYaxshi(1000, 300)
q = pickle.loads(pickle.dumps(e))
print(f"    Asl:       {e!r}, kerak={e.kerak}, bor={e.bor}")
print(f"    Pickle'dan: {q!r}, kerak={q.kerak}, bor={q.bor}")
print(f"    str teng:  {str(e) == str(q)}")

print(f"\n  ⭐ Muqobil — __reduce__:")


class ReduceBilan(Exception):
    def __init__(self, kerak, bor):
        self.kerak, self.bor = kerak, bor
        super().__init__(f"{kerak - bor} yetishmayapti")

    def __reduce__(self):
        return (type(self), (self.kerak, self.bor))     # ⭐


e = ReduceBilan(1000, 300)
q = pickle.loads(pickle.dumps(e))
print(f"    str(e) = {str(e)!r}")
print(f"    pickle → {str(q)!r}, kerak={q.kerak}   ✅")


print("\n\n=== ⚠️ 3. KeyError dan meros ===\n")


class SozlamaKeyError(KeyError):
    """⚠️ str(e) tirnoq bilan."""


class SozlamaKeyErrorTuzatilgan(KeyError):
    """✅ __str__ qayta aniqlangan."""

    def __str__(self):
        return self.args[0] if self.args else ""


class SozlamaException(Exception):
    """✅ Exception dan — muammo yo'q."""


print(f"  {'Sinf':<32} {'str(e)':<28} {'KeyError mi'}")
print("  " + "─" * 74)
for Sinf in (SozlamaKeyError, SozlamaKeyErrorTuzatilgan,
             SozlamaException):
    e = Sinf("Sozlama topilmadi: port")
    k = isinstance(e, KeyError)
    print(f"  {Sinf.__name__:<32} {str(e)!r:<28} {k}")

print(f"""
  ⚠️ KeyError.__str__ — repr(args[0]) qaytaradi:

     class KeyError(LookupError):
         def __str__(self):
             if len(self.args) == 1:
                 return repr(self.args[0])      ⭐ TIRNOQ qo'shadi
             return super().__str__()

  ⭐ SABAB: KeyError("a b") → xabar "a b" mi yoki kalit 'a b' mi?
     repr aniqlik kiritadi.

  ⚠️ Lekin o'z istisnongizda bu CHALKASH bo'ladi.
""")

print(f"  Boshqa o'rnatilgan turlar:")
for Sinf, args in [(ValueError, ("xabar",)), (TypeError, ("xabar",)),
                   (KeyError, ("xabar",)), (IndexError, ("xabar",)),
                   (AttributeError, ("xabar",)),
                   (OSError, (2, "Fayl yo'q"))]:
    e = Sinf(*args)
    print(f"    {Sinf.__name__:<18} str = {str(e)!r}")


print("\n\n=== ⚠️ 4. BaseException dan meros ===\n")


class YomonAsos(BaseException):
    """⚠️ HECH QACHON."""


def ish():
    raise YomonAsos("xato")


print(f"  except Exception: bilan:")
try:
    ish()
    print(f"    ⚠️")
except Exception:
    print(f"    ushlandi")
except BaseException as e:
    print(f"    ⚠️ O'TIB KETDI: {type(e).__name__}")

print(f"""
  ⚠️ NEGA YOMON:

     • except Exception: uni USHLAMAYDI
     • Foydalanuvchi kutmaydi
     • KeyboardInterrupt bilan bir toifada
     • Framework lar to'g'ri ishlamaydi

  ⭐ BaseException FAQAT:
     SystemExit, KeyboardInterrupt, GeneratorExit
     (dastur oqimini boshqarish uchun)

  ⚠️ ISTISNO: agar sizning istisnongiz HAQIQATAN
     dastur oqimini to'xtatishi kerak bo'lsa —
     lekin bu deyarli hech qachon shunday emas.
""")


print("=== 5. Atributlar va __str__ ===\n")


class SorovXatosi(Exception):
    """⭐ Boy kontekst."""

    def __init__(self, xabar: str, *, url: str = "", kod: int = 0,
                 usul: str = "GET", urinish: int = 1,
                 davomiylik: float = 0.0):
        self.url = url
        self.kod = kod
        self.usul = usul
        self.urinish = urinish
        self.davomiylik = davomiylik
        super().__init__(xabar)

    @property
    def xabar(self) -> str:
        return self.args[0] if self.args else ""

    @property
    def qayta_urinsa_boladimi(self) -> bool:
        """⭐ Dasturiy qaror."""
        return self.kod in (408, 429, 500, 502, 503, 504)

    def __str__(self):
        q = [self.xabar]
        if self.kod:
            q.append(f"HTTP {self.kod}")
        if self.usul and self.url:
            q.append(f"{self.usul} {self.url}")
        if self.urinish > 1:
            q.append(f"urinish {self.urinish}")
        return " | ".join(q)

    def __repr__(self):
        return (f"{type(self).__name__}({self.xabar!r}, "
                f"url={self.url!r}, kod={self.kod}, "
                f"usul={self.usul!r})")

    def lugat(self) -> dict:
        """⭐ Serializatsiya uchun."""
        return {
            "tur": type(self).__name__,
            "xabar": self.xabar,
            "url": self.url,
            "kod": self.kod,
            "usul": self.usul,
            "urinish": self.urinish,
            "davomiylik": round(self.davomiylik, 3),
            "qayta_urinsa_boladimi": self.qayta_urinsa_boladimi,
        }


XATOLAR = [
    SorovXatosi("Xizmat mavjud emas", url="https://api.wisar.uz/xodimlar",
                kod=503, urinish=3, davomiylik=12.456),
    SorovXatosi("Topilmadi", url="/api/xodim/99", kod=404),
    SorovXatosi("Ulanib bo'lmadi", url="https://api.wisar.uz", usul="POST"),
]

for e in XATOLAR:
    print(f"  str:  {e}")
    print(f"  repr: {e!r}")
    print(f"  qayta urinish: {e.qayta_urinsa_boladimi}")
    print(f"  lugat: {e.lugat()}")
    print()


print("=== ⭐ 6. Dasturiy ishlov ===\n")


def sorov_bajar(e: SorovXatosi) -> str:
    """⭐ Atributlar asosida qaror."""
    if e.qayta_urinsa_boladimi and e.urinish < 5:
        return f"🔄 Qayta urinish ({e.urinish + 1}-marta)"
    if e.kod == 404:
        return "📭 Resurs topilmadi — urinishsiz"
    if e.kod in (401, 403):
        return "🔒 Autentifikatsiya kerak"
    if e.kod >= 500:
        return "⚠️ Server xatosi — jurnalga"
    if not e.kod:
        return "🌐 Tarmoq muammosi — qayta urinish"
    return "❓ Noma'lum"


for e in XATOLAR:
    print(f"  {str(e)[:52]:<54} {sorov_bajar(e)}")

print(f"""
  ⭐ ATRIBUTLAR NEGA MUHIM:

     str(e) — inson uchun
     e.kod, e.url — DASTUR uchun

     Xabarni tahlil qilish (parsing) ANTIPATTERN:
       if "503" in str(e):      ❌ mo'rt
       if e.kod == 503:         ✅
""")


print("=== ⚠️ 7. Keng tarqalgan xatolar ===\n")

print("""  ❌ 1. Yon ta'sir konstruktorda:

     class Xato(Exception):
         def __init__(self, m):
             jurnal.error(m)                ⚠️ istisno yaratilganda
             xabar_yubor(m)                 ⚠️ hatto ushlansa ham
             super().__init__(m)

     ⚠️ MUAMMO:
        • Istisno yaratilishi ≠ ko'tarilishi
        • Testlarda xabar yuboriladi
        • pickle da qayta chaqiriladi

     ✅ Yon ta'sirni except ichida qiling.


  ❌ 2. O'zgaruvchan sukut:

     class Xato(Exception):
         def __init__(self, m, teglar=[]):  ⚠️ 7.3-dars tuzog'i
             self.teglar = teglar

     ✅ teglar=None → self.teglar = list(teglar or [])


  ❌ 3. Nom kelishuvi:

     class Muammo(Exception)                ⚠️
     class XatoHolat(Exception)             ⚠️
     class TolovXatosi(Exception)           ✅ (o'zbekcha)
     class PaymentError(Exception)          ✅ (inglizcha)

     ⭐ PEP 8: "Error" bilan tugasin (inglizcha loyihalarda)


  ❌ 4. Juda ko'p sinf:

     class YuklashXatosi(Exception)
     class TekshirishXatosi(Exception)      ⚠️ har funksiya uchun
     class OgirishXatosi(Exception)
     class SaqlashXatosi(Exception)

     ✅ Bitta sinf + bosqich atributi:
        class QuvurXatosi(Exception):
            def __init__(self, m, bosqich): ...


  ❌ 5. Bog'lanmagan sinflar:

     class XatoA(Exception)                 ⚠️ umumiy asos yo'q
     class XatoB(Exception)

     ✅ class KutubxonaXatosi(Exception)
        class XatoA(KutubxonaXatosi)
""")

print(f"  Yon ta'sir muammosi namoyishi:\n")

CHAQIRUVLAR = []


class YonTasirli(Exception):
    def __init__(self, m):
        CHAQIRUVLAR.append(m)               # ⚠️ yon ta'sir
        super().__init__(m)


print(f"    e = YonTasirli('xato')  (KO'TARILMAYDI)")
e = YonTasirli("xato")
print(f"      CHAQIRUVLAR = {CHAQIRUVLAR}   ⚠️ yozildi!")

CHAQIRUVLAR.clear()
q = pickle.loads(pickle.dumps(e))
print(f"\n    pickle.loads(...)")
print(f"      CHAQIRUVLAR = {CHAQIRUVLAR}   ⚠️ yana yozildi!")

print(f"""
  ⭐ QOIDA: istisno konstruktori — FAQAT ma'lumot saqlash.
     Jurnal, xabar, tozalash — except blokida.
""")

Natijaning muhim qismi:

text
=== ⚠️ 2. super().__init__() unutish ===

  Sinf           str(e)                     e.args               pickle
  ────────────────────────────────────────────────────────────────────────
  Yomon          '(1000, 300)'              (1000, 300)          ✅
  Yaxshi         '700 yetishmayapti'        ('700 yetishmayapti',) ❌ TypeError
  EngYaxshi      '700 yetishmayapti'        (1000, 300)          ✅

=== ⚠️ 3. KeyError dan meros ===

  Sinf                             str(e)                       KeyError mi
  ──────────────────────────────────────────────────────────────────────────
  SozlamaKeyError                  "'Sozlama topilmadi: port'"  True
  SozlamaKeyErrorTuzatilgan        'Sozlama topilmadi: port'    True
  SozlamaException                 'Sozlama topilmadi: port'    False

=== ⚠️ 4. BaseException dan meros ===

  except Exception: bilan:
    ⚠️ O'TIB KETDI: YomonAsos

=== ⚠️ 7. Keng tarqalgan xatolar ===

    e = YonTasirli('xato')  (KO'TARILMAYDI)
      CHAQIRUVLAR = ['xato']   ⚠️ yozildi!

Nima ko'rsatdi: 2.1, 2.3, 2.4, 2.5, 2.7-bo'limlar.

Misol 2 — Kutubxona istisnolari

python
"""Kutubxona uchun istisno ierarxiyasini loyihalash."""

from __future__ import annotations

import json
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any

print("=== WISAR KUTUBXONASI ISTISNOLARI ===\n")


# ═══ ASOS ═══

class WisarXatosi(Exception):
    """Barcha wisar xatolarining asosi.

    ⭐ Foydalanuvchi HAMMASINI bitta blok bilan ushlashi mumkin:
        except WisarXatosi: ...
    """

    #: Foydalanuvchiga ko'rsatiladigan sukut xabar
    SUKUT_XABAR: str = "Noma'lum xato"
    #: HTTP javob kodi
    HTTP_KODI: int = 500
    #: Qayta urinish mumkinmi
    QAYTA_URINISH: bool = False

    def __init__(self, xabar: str = "", **kontekst: Any):
        self.kontekst = kontekst
        self.vaqt = datetime.now()
        super().__init__(xabar or self.SUKUT_XABAR)

    @property
    def xabar(self) -> str:
        return self.args[0] if self.args else self.SUKUT_XABAR

    def __str__(self) -> str:
        if not self.kontekst:
            return self.xabar
        k = ", ".join(f"{a}={b!r}" for a, b in self.kontekst.items())
        return f"{self.xabar} ({k})"

    def __repr__(self) -> str:
        k = "".join(f", {a}={b!r}" for a, b in self.kontekst.items())
        return f"{type(self).__name__}({self.xabar!r}{k})"

    def lugat(self) -> dict[str, Any]:
        """⭐ API javobi uchun."""
        return {
            "xato": type(self).__name__,
            "xabar": self.xabar,
            "kod": self.HTTP_KODI,
            "qayta_urinish": self.QAYTA_URINISH,
            "vaqt": self.vaqt.isoformat(),
            **({"kontekst": self.kontekst} if self.kontekst else {}),
        }

    def json(self) -> str:
        return json.dumps(self.lugat(), ensure_ascii=False, default=str)


# ═══ TOIFALAR ═══

class SozlamaXatosi(WisarXatosi):
    """Konfiguratsiya bilan bog'liq."""
    SUKUT_XABAR = "Sozlama xatosi"
    HTTP_KODI = 500


class TarmoqXatosi(WisarXatosi):
    """Tarmoq va tashqi xizmatlar."""
    SUKUT_XABAR = "Tarmoq xatosi"
    HTTP_KODI = 503
    QAYTA_URINISH = True


class MalumotXatosi(WisarXatosi):
    """Foydalanuvchi ma'lumoti."""
    SUKUT_XABAR = "Ma'lumot xatosi"
    HTTP_KODI = 400


class RuxsatXatosi(WisarXatosi):
    """Autentifikatsiya va avtorizatsiya."""
    SUKUT_XABAR = "Ruxsat yo'q"
    HTTP_KODI = 403


class ResursXatosi(WisarXatosi):
    """Tizim resurslari."""
    SUKUT_XABAR = "Resurs mavjud emas"
    HTTP_KODI = 507


# ═══ ANIQ XATOLAR ═══

class SozlamaTopilmadi(SozlamaXatosi):
    def __init__(self, kalit: str, mavjud: list[str] | None = None):
        self.kalit = kalit
        self.mavjud = list(mavjud or [])
        super().__init__(
            f"Sozlama topilmadi: {kalit}",
            kalit=kalit,
            **({"mavjud": self.mavjud[:5]} if self.mavjud else {}),
        )


class SozlamaBuzilgan(SozlamaXatosi):
    def __init__(self, fayl: str, qator: int = 0, sabab: str = ""):
        self.fayl, self.qator = fayl, qator
        joy = f":{qator}" if qator else ""
        super().__init__(
            f"Sozlama fayli buzilgan: {fayl}{joy}"
            + (f" — {sabab}" if sabab else ""),
            fayl=fayl, qator=qator,
        )


class UlanmadiXato(TarmoqXatosi):
    def __init__(self, manzil: str, urinish: int = 1):
        self.manzil, self.urinish = manzil, urinish
        super().__init__(
            f"Ulanib bo'lmadi: {manzil}",
            manzil=manzil, urinish=urinish,
        )


class VaqtTugadi(TarmoqXatosi):
    HTTP_KODI = 504

    def __init__(self, amal: str, sekund: float):
        self.amal, self.sekund = amal, sekund
        super().__init__(
            f"Vaqt tugadi: {amal} ({sekund}s)",
            amal=amal, sekund=sekund,
        )


class TezlikChegarasi(TarmoqXatosi):
    HTTP_KODI = 429

    def __init__(self, chegara: int, oyna: str = "daqiqa",
                 kutish: float = 60.0):
        self.chegara, self.oyna, self.kutish = chegara, oyna, kutish
        super().__init__(
            f"Tezlik chegarasi: {chegara}/{oyna}",
            chegara=chegara, kutish=kutish,
        )


class ValidatsiyaXatosi(MalumotXatosi):
    def __init__(self, maydon: str, sabab: str, qiymat: Any = None):
        self.maydon, self.sabab, self.qiymat = maydon, sabab, qiymat
        super().__init__(
            f"{maydon}: {sabab}",
            maydon=maydon, **({"qiymat": qiymat}
                              if qiymat is not None else {}),
        )


class TopilmadiXatosi(MalumotXatosi):
    HTTP_KODI = 404

    def __init__(self, tur: str, id_: Any):
        self.tur, self.id = tur, id_
        super().__init__(f"{tur} topilmadi: {id_}", tur=tur, id=id_)


class TakrorXatosi(MalumotXatosi):
    HTTP_KODI = 409

    def __init__(self, tur: str, maydon: str, qiymat: Any):
        self.tur, self.maydon, self.qiymat = tur, maydon, qiymat
        super().__init__(
            f"{tur} allaqachon mavjud: {maydon}={qiymat!r}",
            tur=tur, maydon=maydon,
        )


class TokenMuddati(RuxsatXatosi):
    HTTP_KODI = 401

    def __init__(self, tugagan: datetime | None = None):
        self.tugagan = tugagan
        super().__init__(
            "Token muddati tugagan",
            **({"tugagan": tugagan.isoformat()} if tugagan else {}),
        )


class HuquqYetarliEmas(RuxsatXatosi):
    def __init__(self, kerak: str, bor: list[str] | None = None):
        self.kerak, self.bor = kerak, list(bor or [])
        super().__init__(
            f"Huquq kerak: {kerak}",
            kerak=kerak, **({"bor": self.bor} if self.bor else {}),
        )


class JoyYetarliEmas(ResursXatosi):
    def __init__(self, kerak_mb: float, bor_mb: float):
        self.kerak_mb, self.bor_mb = kerak_mb, bor_mb
        self.yetishmayapti = kerak_mb - bor_mb
        super().__init__(
            f"Disk joyi yetarli emas: {self.yetishmayapti:.1f} MB kerak",
            kerak=kerak_mb, bor=bor_mb,
        )


# ═══ IERARXIYA ═══

print("1. Ierarxiya:\n")


def ierarxiya(Sinf: type, chuqurlik: int = 0) -> list[str]:
    q = [f"{'    ' * chuqurlik}{'└── ' if chuqurlik else ''}"
         f"{Sinf.__name__:<22} HTTP {getattr(Sinf, 'HTTP_KODI', '?')}"
         f"{'  🔄' if getattr(Sinf, 'QAYTA_URINISH', False) else ''}"]
    for a in sorted(Sinf.__subclasses__(), key=lambda c: c.__name__):
        q.extend(ierarxiya(a, chuqurlik + 1))
    return q


for q in ierarxiya(WisarXatosi):
    print(f"  {q}")


print("\n\n2. ⭐ Foydalanuvchi tanlaydi:\n")

XATOLAR = [
    SozlamaTopilmadi("prot", ["host", "port", "debug", "til"]),
    SozlamaBuzilgan("app.json", 12, "Expecting ','"),
    UlanmadiXato("db.wisar.uz:5432", urinish=3),
    VaqtTugadi("baza so'rovi", 30.0),
    TezlikChegarasi(100, "daqiqa", 45.0),
    ValidatsiyaXatosi("email", "format noto'g'ri", "notogri"),
    TopilmadiXatosi("Xodim", 999),
    TakrorXatosi("Xodim", "email", "aziz@wisar.uz"),
    TokenMuddati(datetime(2026, 1, 1)),
    HuquqYetarliEmas("admin", ["oquvchi", "muharrir"]),
    JoyYetarliEmas(500.0, 123.4),
]

print(f"  Aniq turlar bilan:\n")
for e in XATOLAR[:4]:
    print(f"    {type(e).__name__:<20} {e}")

print(f"\n  Toifalar bilan:\n")
for Toifa in (SozlamaXatosi, TarmoqXatosi, MalumotXatosi,
              RuxsatXatosi, ResursXatosi):
    mos = [e for e in XATOLAR if isinstance(e, Toifa)]
    print(f"    except {Toifa.__name__:<18} → {len(mos)} xato: "
          f"{[type(e).__name__ for e in mos]}")

print(f"\n  Hammasi:\n")
print(f"    except WisarXatosi → {len(XATOLAR)} xato")


print("\n\n3. ⭐ HTTP javoblar:\n")

print(f"  {'Istisno':<22} {'HTTP':<7} {'Qayta urin':<12} {'Xabar'}")
print("  " + "─" * 76)
for e in XATOLAR:
    print(f"  {type(e).__name__:<22} {e.HTTP_KODI:<7} "
          f"{'🔄 ha' if e.QAYTA_URINISH else '—':<12} "
          f"{str(e)[:32]}")


print("\n\n4. Serializatsiya:\n")

for e in XATOLAR[:3]:
    print(f"  {type(e).__name__}:")
    print(f"    {e.json()}")
    print()


print("5. ⭐ Dasturiy ishlov:\n")


def ishlov(e: WisarXatosi) -> dict:
    """⭐ Atributlar asosida aniq qaror."""
    javob = e.lugat()

    if isinstance(e, TezlikChegarasi):
        javob["retry_after"] = e.kutish
        javob["maslahat"] = f"{e.kutish:.0f} soniyadan keyin urinib ko'ring"
    elif isinstance(e, UlanmadiXato):
        javob["maslahat"] = (
            "Tarmoq ulanishini tekshiring"
            if e.urinish >= 3 else "Qayta urinilmoqda"
        )
    elif isinstance(e, SozlamaTopilmadi) and e.mavjud:
        import difflib
        yaqin = difflib.get_close_matches(e.kalit, e.mavjud, n=1)
        if yaqin:
            javob["maslahat"] = f"Balki: {yaqin[0]!r}?"
    elif isinstance(e, ValidatsiyaXatosi):
        javob["maydon"] = e.maydon
    elif isinstance(e, JoyYetarliEmas):
        javob["maslahat"] = (
            f"{e.yetishmayapti:.0f} MB bo'shatish kerak"
        )
    elif isinstance(e, TokenMuddati):
        javob["maslahat"] = "Qaytadan kiring"

    return javob


for e in [XATOLAR[0], XATOLAR[2], XATOLAR[4], XATOLAR[10]]:
    j = ishlov(e)
    print(f"  {type(e).__name__}:")
    print(f"    HTTP {j['kod']}: {j['xabar']}")
    if "maslahat" in j:
        print(f"    💡 {j['maslahat']}")
    if "retry_after" in j:
        print(f"    ⏱ Retry-After: {j['retry_after']}")
    print()


print("6. ⭐ O'rnatilgan turlar bilan moslik:\n")


class SozlamaKalitYoq(SozlamaXatosi, KeyError):
    """⭐ Ikkalasi bilan ham ushlanadi."""

    def __init__(self, kalit: str):
        self.kalit = kalit
        SozlamaXatosi.__init__(self, f"Kalit yo'q: {kalit}",
                               kalit=kalit)

    def __str__(self):
        return SozlamaXatosi.__str__(self)      # ⭐ KeyError emas


e = SozlamaKalitYoq("port")

print(f"  MRO: {[c.__name__ for c in type(e).__mro__[:5]]}\n")
print(f"  {'Ushlash usuli':<28} {'Ishlaydimi'}")
print("  " + "─" * 44)
for Tur in (SozlamaKalitYoq, SozlamaXatosi, WisarXatosi,
            KeyError, LookupError, Exception):
    print(f"  except {Tur.__name__:<20} {isinstance(e, Tur)}")

print(f"\n  str(e) = {str(e)!r}   ⭐ tirnoqsiz (__str__ tuzatilgan)")

print(f"""
  ⭐ KO'P MEROS FOYDASI:
     • Eski kod: except KeyError: → ishlaydi
     • Yangi kod: except SozlamaXatosi: → ham ishlaydi
     • Migratsiya oson

  ⚠️ EHTIYOT:
     • MRO murakkablashadi
     • __str__ ni qayta aniqlash kerak
     • __init__ ni aniq chaqirish kerak
""")

Natijaning muhim qismi:

text
1. Ierarxiya:

  WisarXatosi            HTTP 500
      └── MalumotXatosi      HTTP 400
          └── TakrorXatosi       HTTP 409
          └── TopilmadiXatosi    HTTP 404
          └── ValidatsiyaXatosi  HTTP 400
      └── ResursXatosi       HTTP 507
      └── RuxsatXatosi       HTTP 403
      └── SozlamaXatosi      HTTP 500
      └── TarmoqXatosi       HTTP 503  🔄
          └── TezlikChegarasi    HTTP 429  🔄
          └── UlanmadiXato       HTTP 503  🔄
          └── VaqtTugadi         HTTP 504  🔄

2. ⭐ Foydalanuvchi tanlaydi:

    except TarmoqXatosi      → 3 xato: ['UlanmadiXato', 'VaqtTugadi',
                                        'TezlikChegarasi']
    except WisarXatosi → 11 xato

5. ⭐ Dasturiy ishlov:

  SozlamaTopilmadi:
    HTTP 500: Sozlama topilmadi: prot
    💡 Balki: 'port'?

Nima ko'rsatdi: 2.2, 2.4, 2.6-bo'limlar.

Misol 3 — Loyihalash namunalari

python
"""Turli holatlar uchun istisno loyihalari."""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from datetime import date, datetime
from decimal import Decimal
from enum import Enum
from typing import Any, ClassVar

print("=== 1. Oddiy vs boy istisno ===\n")


class OddiyXato(Exception):
    """✅ Yetarli — atribut kerak emas."""


class BoyXato(Exception):
    """✅ Atributlar dasturiy qaror uchun kerak."""

    def __init__(self, xabar: str, *, kod: str, tuzatish: str = ""):
        self.kod, self.tuzatish = kod, tuzatish
        super().__init__(xabar)


print("""  ⭐ QACHON ODDIY:
     • Faqat xabar kerak
     • Dasturiy qaror yo'q
     • Bir joyda ushlanadi

  ⭐ QACHON BOY:
     • Dastur atributga qarab qaror qiladi
     • Foydalanuvchiga tuzatish taklifi
     • Serializatsiya (API javob)
     • Statistika va guruhlash
""")


print("=== 2. ⭐ Xato kodlari ===\n")


class XatoKodi(str, Enum):
    """⭐ Barqaror kodlar — xabar o'zgarsa ham kod qoladi."""

    SOZLAMA_YOQ = "E1001"
    SOZLAMA_BUZILGAN = "E1002"
    TARMOQ_ULANMADI = "E2001"
    TARMOQ_VAQT = "E2002"
    MALUMOT_NOTOGRI = "E3001"
    MALUMOT_TAKROR = "E3002"
    RUXSAT_YOQ = "E4001"

    def __str__(self): return self.value


class KodliXato(Exception):
    """⭐ Kod bilan — hujjatlarda havola qilish mumkin."""

    KOD: ClassVar[XatoKodi]

    def __init__(self, xabar: str = "", **kontekst):
        self.kontekst = kontekst
        super().__init__(xabar or self.__doc__ or "")

    def __str__(self):
        return f"[{self.KOD}] {self.args[0]}"

    @property
    def hujjat_url(self) -> str:
        return f"https://docs.wisar.uz/xatolar/{self.KOD}"


class SozlamaYoq(KodliXato):
    """Sozlama fayli topilmadi."""
    KOD = XatoKodi.SOZLAMA_YOQ


class TarmoqUlanmadi(KodliXato):
    """Tashqi xizmatga ulanib bo'lmadi."""
    KOD = XatoKodi.TARMOQ_ULANMADI


class MalumotTakror(KodliXato):
    """Bunday yozuv allaqachon mavjud."""
    KOD = XatoKodi.MALUMOT_TAKROR


for Sinf in (SozlamaYoq, TarmoqUlanmadi, MalumotTakror):
    e = Sinf()
    print(f"  {e}")
    print(f"    Hujjat: {e.hujjat_url}")

print(f"""
  ⭐ XATO KODLARI FOYDASI:

     • Xabar TARJIMA qilinadi, kod O'ZGARMAYDI
     • Foydalanuvchi qidiruvda ishlatadi
     • Hujjatga havola
     • Qo'llab-quvvatlash xizmati aniq biladi
     • Jurnalda guruhlash oson

  ⭐ Misollar: PostgreSQL (23505), HTTP (404), AWS (AccessDenied)
""")


print("=== 3. ⭐ Ko'p tilli xabarlar ===\n")


class TarjimaliXato(Exception):
    """⭐ Xabar TILGA bog'liq, kod emas."""

    XABARLAR: ClassVar[dict[str, str]] = {}
    SUKUT_TIL: ClassVar[str] = "uz"

    def __init__(self, til: str = "", **params):
        self.til = til or self.SUKUT_TIL
        self.params = params
        super().__init__(self.tarjima(self.til))

    def tarjima(self, til: str) -> str:
        shablon = self.XABARLAR.get(
            til, self.XABARLAR.get(self.SUKUT_TIL, "")
        )
        try:
            return shablon.format(**self.params)
        except KeyError:
            return shablon


class TokenTugadi(TarjimaliXato):
    XABARLAR = {
        "uz": "Sessiya muddati tugadi. Qaytadan kiring.",
        "ru": "Сессия истекла. Войдите заново.",
        "en": "Session expired. Please log in again.",
    }


class YetarliEmas(TarjimaliXato):
    XABARLAR = {
        "uz": "{nima} yetarli emas: {kerak} kerak, {bor} bor",
        "ru": "Недостаточно {nima}: нужно {kerak}, есть {bor}",
        "en": "Insufficient {nima}: need {kerak}, have {bor}",
    }


print(f"  Bir xato, uch til:\n")
for til in ("uz", "ru", "en"):
    e = TokenTugadi(til)
    print(f"    {til}: {e}")

print(f"\n  Parametrlar bilan:\n")
for til in ("uz", "ru", "en"):
    e = YetarliEmas(til, nima="mablag'", kerak="1,000,000",
                    bor="300,000")
    print(f"    {til}: {e}")

print(f"""
  ⭐ TARJIMA STRATEGIYASI:
     • Istisno ichida shablon
     • Parametrlar alohida
     • Til so'rov paytida aniqlanadi

  ⚠️ MUQOBIL: xabarni istisno TASHQARISIDA tarjima qilish
     (gettext, i18n kutubxonalari)
""")


print("=== 4. ⭐ Validatsiya istisnolari ===\n")


@dataclass
class MaydonXatosi:
    """Bitta maydon xatosi."""
    yol: str
    sabab: str
    qiymat: Any = None
    kod: str = ""

    def __str__(self):
        q = f"{self.yol}: {self.sabab}"
        if self.qiymat is not None:
            q += f" (qiymat: {self.qiymat!r})"
        return q


class ValidatsiyaXatosi(Exception):
    """⭐ BIR NECHTA maydon xatosini saqlaydi."""

    def __init__(self, xatolar: list[MaydonXatosi] | MaydonXatosi):
        if isinstance(xatolar, MaydonXatosi):
            xatolar = [xatolar]
        self.xatolar = list(xatolar)
        super().__init__(f"{len(self.xatolar)} ta validatsiya xatosi")

    def __len__(self):
        return len(self.xatolar)

    def __iter__(self):
        return iter(self.xatolar)

    def __str__(self):
        if len(self.xatolar) == 1:
            return str(self.xatolar[0])
        return (f"{len(self.xatolar)} ta xato:\n  "
                + "\n  ".join(str(e) for e in self.xatolar))

    def maydonlar(self) -> dict[str, list[str]]:
        """⭐ Frontend uchun: maydon → xatolar."""
        natija: dict[str, list[str]] = {}
        for e in self.xatolar:
            natija.setdefault(e.yol, []).append(e.sabab)
        return natija

    def lugat(self) -> dict:
        return {
            "xato": "ValidatsiyaXatosi",
            "soni": len(self.xatolar),
            "maydonlar": self.maydonlar(),
            "tafsilotlar": [
                {"yol": e.yol, "sabab": e.sabab, "kod": e.kod}
                for e in self.xatolar
            ],
        }


def xodim_tekshir(m: dict) -> dict:
    """⭐ BARCHA xatolarni yig'adi."""
    xatolar: list[MaydonXatosi] = []

    if not m.get("ism", "").strip():
        xatolar.append(MaydonXatosi("ism", "majburiy maydon",
                                    kod="required"))
    elif len(m["ism"]) < 2:
        xatolar.append(MaydonXatosi("ism", "kamida 2 belgi",
                                    m["ism"], "min_length"))

    email = m.get("email", "")
    if not email:
        xatolar.append(MaydonXatosi("email", "majburiy maydon",
                                    kod="required"))
    elif not re.fullmatch(r"[^@\s]+@[^@\s]+\.[a-z]{2,}", email):
        xatolar.append(MaydonXatosi("email", "format noto'g'ri",
                                    email, "format"))

    maosh = m.get("maosh")
    if maosh is None:
        xatolar.append(MaydonXatosi("maosh", "majburiy maydon",
                                    kod="required"))
    elif not isinstance(maosh, (int, float)):
        xatolar.append(MaydonXatosi("maosh", "son bo'lsin",
                                    maosh, "type"))
    elif maosh < 500_000:
        xatolar.append(MaydonXatosi("maosh", "kamida 500,000",
                                    maosh, "min"))

    if "manzil" in m:
        manzil = m["manzil"]
        if not isinstance(manzil, dict):
            xatolar.append(MaydonXatosi("manzil", "obyekt bo'lsin",
                                        manzil, "type"))
        else:
            if not manzil.get("shahar"):
                xatolar.append(MaydonXatosi("manzil.shahar",
                                            "majburiy", kod="required"))

    if xatolar:
        raise ValidatsiyaXatosi(xatolar)
    return m


SINOVLAR = [
    {"ism": "Aziz Karimov", "email": "aziz@wisar.uz", "maosh": 8_500_000},
    {"ism": "A", "email": "notogri", "maosh": 100},
    {"manzil": {"kocha": "Amir Temur"}},
]

for i, m in enumerate(SINOVLAR, 1):
    print(f"  Sinov {i}:")
    try:
        xodim_tekshir(m)
        print(f"    ✅ Muvaffaqiyatli")
    except ValidatsiyaXatosi as e:
        print(f"    ❌ {len(e)} ta xato:")
        for x in e:
            print(f"       • {x}")
        print(f"    Frontend uchun: {e.maydonlar()}")
    print()


print("=== 5. ⭐ Qayta urinish ma'lumoti ===\n")


class QaytaUrinishMumkin(Exception):
    """⭐ Qayta urinish strategiyasini o'zi biladi."""

    ENG_KOP_URINISH: ClassVar[int] = 3
    ASOSIY_KUTISH: ClassVar[float] = 1.0

    def __init__(self, xabar: str, *, urinish: int = 1,
                 kutish: float | None = None):
        self.urinish = urinish
        self._kutish = kutish
        super().__init__(xabar)

    @property
    def yana_urinsa_boladimi(self) -> bool:
        return self.urinish < self.ENG_KOP_URINISH

    @property
    def kutish(self) -> float:
        """⭐ Eksponensial kutish."""
        if self._kutish is not None:
            return self._kutish
        return self.ASOSIY_KUTISH * (2 ** (self.urinish - 1))

    def keyingi(self) -> "QaytaUrinishMumkin":
        """⭐ Keyingi urinish uchun nusxa."""
        return type(self)(self.args[0], urinish=self.urinish + 1,
                          kutish=self._kutish)


class TarmoqVaqt(QaytaUrinishMumkin):
    ENG_KOP_URINISH = 5
    ASOSIY_KUTISH = 0.5


class TezlikChegarasi(QaytaUrinishMumkin):
    ENG_KOP_URINISH = 3
    ASOSIY_KUTISH = 60.0


print(f"  {'Istisno':<20} {'Urinish':<10} {'Yana':<8} {'Kutish'}")
print("  " + "─" * 52)
for Sinf in (TarmoqVaqt, TezlikChegarasi):
    e = Sinf("xato")
    while True:
        print(f"  {Sinf.__name__:<20} {e.urinish}/"
              f"{Sinf.ENG_KOP_URINISH:<8} "
              f"{'✅' if e.yana_urinsa_boladimi else '❌':<8} "
              f"{e.kutish:.1f}s")
        if not e.yana_urinsa_boladimi:
            break
        e = e.keyingi()
    print()

print(f"  ⭐ Retry-After sarlavhasi bilan:")
e = TezlikChegarasi("Chegara oshdi", kutish=45.0)
print(f"    {e} → Retry-After: {e.kutish:.0f}")

print(f"""
  ⭐ ISTISNO O'ZI STRATEGIYANI BILADI:

     except QaytaUrinishMumkin as e:
         if e.yana_urinsa_boladimi:
             time.sleep(e.kutish)
             raise e.keyingi()      ⭐ urinish oshadi
         raise

  ⭐ Har istisno turi o'z sozlamalari bilan.
""")


print("=== 6. ⭐ Istisno fabrikasi ===\n")


def xato_yasa(nom: str, asos: type = Exception, *,
              hujjat: str = "", kod: str = "",
              http: int = 500) -> type:
    """⭐ Bir xil naqshdagi istisnolarni yaratadi."""
    return type(nom, (asos,), {
        "__doc__": hujjat or f"{nom} xatosi.",
        "KOD": kod or nom.upper(),
        "HTTP_KODI": http,
    })


class ApiXatosi(Exception):
    KOD = "API_ERROR"
    HTTP_KODI = 500


# ⭐ Bir necha o'xshash istisno — bir qatorda
NotFound = xato_yasa("NotFound", ApiXatosi, http=404,
                     hujjat="Resurs topilmadi.")
Conflict = xato_yasa("Conflict", ApiXatosi, http=409,
                     hujjat="Ziddiyat.")
TooManyRequests = xato_yasa("TooManyRequests", ApiXatosi, http=429,
                            hujjat="Juda ko'p so'rov.")
Unauthorized = xato_yasa("Unauthorized", ApiXatosi, http=401,
                         hujjat="Autentifikatsiya kerak.")

print(f"  {'Sinf':<20} {'Kod':<20} {'HTTP':<7} {'Hujjat'}")
print("  " + "─" * 68)
for Sinf in (NotFound, Conflict, TooManyRequests, Unauthorized):
    print(f"  {Sinf.__name__:<20} {Sinf.KOD:<20} "
          f"{Sinf.HTTP_KODI:<7} {Sinf.__doc__}")

print(f"\n  Ishlatish:")
try:
    raise NotFound("Xodim topilmadi: 999")
except ApiXatosi as e:
    print(f"    except ApiXatosi → {type(e).__name__}: {e}")
    print(f"    HTTP {e.HTTP_KODI}")

print(f"""
  ⚠️ FABRIKA — EHTIYOT BILAN:

     ✅ Ko'p bir xil naqshli istisno
     ✅ Kod generatsiyasi (OpenAPI dan)

     ❌ IDE avtoto'ldirish ishlamaydi
     ❌ mypy tushunmaydi
     ❌ Manba kodda topib bo'lmaydi

  ⭐ Odatda ODDIY sinf e'loni yaxshiroq:
     class NotFound(ApiXatosi):
         HTTP_KODI = 404
""")

Natijaning muhim qismi:

text
=== 2. ⭐ Xato kodlari ===

  [E1001] Sozlama fayli topilmadi.
    Hujjat: https://docs.wisar.uz/xatolar/E1001
  [E2001] Tashqi xizmatga ulanib bo'lmadi.
    Hujjat: https://docs.wisar.uz/xatolar/E2001

=== 3. ⭐ Ko'p tilli xabarlar ===

    uz: Sessiya muddati tugadi. Qaytadan kiring.
    ru: Сессия истекла. Войдите заново.
    en: Session expired. Please log in again.

=== 4. ⭐ Validatsiya istisnolari ===

  Sinov 2:
    ❌ 3 ta xato:
       • ism: kamida 2 belgi (qiymat: 'A')
       • email: format noto'g'ri (qiymat: 'notogri')
       • maosh: kamida 500,000 (qiymat: 100)
    Frontend uchun: {'ism': ['kamida 2 belgi'], 'email': ["format noto'g'ri"], 'maosh': ['kamida 500,000']}

=== 5. ⭐ Qayta urinish ma'lumoti ===

  Istisno              Urinish    Yana     Kutish
  ────────────────────────────────────────────────────
  TarmoqVaqt           1/5        ✅       0.5s
  TarmoqVaqt           2/5        ✅       1.0s
  TarmoqVaqt           5/5        ❌       8.0s

Nima ko'rsatdi: 2.2, 2.4, 2.6-bo'limlar.

Misol 4 — Amaliy: to'lov tizimi

python
"""To'liq istisno loyihasi — real loyihada."""

from __future__ import annotations

import io
import json
import logging
import re
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from decimal import Decimal
from enum import Enum
from typing import Any, ClassVar

print("=== TO'LOV TIZIMI ISTISNOLARI ===\n")

jurnal = logging.getLogger("tolov")
jurnal.setLevel(logging.DEBUG)
_b = io.StringIO()
_h = logging.StreamHandler(_b)
_h.setFormatter(logging.Formatter("%(levelname)-8s %(message)s"))
jurnal.addHandler(_h)
jurnal.propagate = False


class Ogirlik(str, Enum):
    """Xato jiddiyligi."""
    PAST = "past"           # foydalanuvchi tuzatadi
    ORTA = "orta"           # qayta urinish
    YUQORI = "yuqori"       # dasturchi aralashuvi
    KRITIK = "kritik"       # darhol xabar

    def __str__(self): return self.value


# ═══ ASOS ═══

class TolovXatosi(Exception):
    """To'lov tizimining barcha xatolari.

    ⭐ SINF ATRIBUTLARI — har avlod o'zicha belgilaydi:
        KOD, HTTP_KODI, OGIRLIK, QAYTA_URINISH,
        FOYDALANUVCHIGA, JURNAL_DARAJASI
    """

    KOD: ClassVar[str] = "TOLOV_XATO"
    HTTP_KODI: ClassVar[int] = 500
    OGIRLIK: ClassVar[Ogirlik] = Ogirlik.YUQORI
    QAYTA_URINISH: ClassVar[bool] = False
    FOYDALANUVCHIGA: ClassVar[str] = "To'lovda xato yuz berdi"
    JURNAL_DARAJASI: ClassVar[int] = logging.ERROR

    def __init__(self, xabar: str = "", *, tranzaksiya_id: str = "",
                 **kontekst: Any):
        self.tranzaksiya_id = tranzaksiya_id
        self.kontekst = kontekst
        self.vaqt = datetime.now()
        super().__init__(xabar or self.FOYDALANUVCHIGA)

    # ── Ko'rinish ──

    @property
    def texnik_xabar(self) -> str:
        return self.args[0] if self.args else ""

    def __str__(self) -> str:
        q = [f"[{self.KOD}] {self.texnik_xabar}"]
        if self.tranzaksiya_id:
            q.append(f"tx={self.tranzaksiya_id}")
        if self.kontekst:
            q.append(", ".join(f"{k}={v!r}"
                               for k, v in self.kontekst.items()))
        return " | ".join(q)

    def __repr__(self) -> str:
        return (f"{type(self).__name__}({self.texnik_xabar!r}, "
                f"tranzaksiya_id={self.tranzaksiya_id!r})")

    # ── API ──

    def javob(self) -> tuple[int, dict]:
        """⭐ HTTP javobi — sirlar yo'q."""
        tana: dict[str, Any] = {
            "xato": {
                "kod": self.KOD,
                "xabar": self.FOYDALANUVCHIGA,
                "ogirlik": str(self.OGIRLIK),
            }
        }
        if self.tranzaksiya_id:
            tana["xato"]["tranzaksiya_id"] = self.tranzaksiya_id
        if self.QAYTA_URINISH:
            tana["xato"]["qayta_urinish"] = True
        maslahat = self.maslahat()
        if maslahat:
            tana["xato"]["maslahat"] = maslahat
        return self.HTTP_KODI, tana

    def maslahat(self) -> str:
        """⭐ Avlodlar qayta aniqlaydi."""
        return ""

    def jurnalga(self) -> None:
        """⭐ To'g'ri darajada yozish."""
        jurnal.log(
            self.JURNAL_DARAJASI, "%s | %s",
            type(self).__name__, self,
        )

    def lugat(self, sirlar: bool = False) -> dict:
        m = {
            "tur": type(self).__name__,
            "kod": self.KOD,
            "http": self.HTTP_KODI,
            "ogirlik": str(self.OGIRLIK),
            "qayta_urinish": self.QAYTA_URINISH,
            "vaqt": self.vaqt.isoformat(),
            "foydalanuvchiga": self.FOYDALANUVCHIGA,
        }
        if sirlar:
            m["texnik"] = self.texnik_xabar
            m["kontekst"] = self.kontekst
            m["tranzaksiya_id"] = self.tranzaksiya_id
        return m


# ═══ TOIFALAR ═══

class KartaXatosi(TolovXatosi):
    """Karta ma'lumotlari bilan bog'liq."""
    KOD = "KARTA_XATO"
    HTTP_KODI = 400
    OGIRLIK = Ogirlik.PAST
    FOYDALANUVCHIGA = "Karta ma'lumotlari noto'g'ri"
    JURNAL_DARAJASI = logging.INFO


class MablagXatosi(TolovXatosi):
    """Mablag' bilan bog'liq."""
    KOD = "MABLAG_XATO"
    HTTP_KODI = 402
    OGIRLIK = Ogirlik.PAST
    FOYDALANUVCHIGA = "To'lov amalga oshmadi"
    JURNAL_DARAJASI = logging.INFO


class ProtsessingXatosi(TolovXatosi):
    """To'lov protsessingi bilan bog'liq."""
    KOD = "PROTSESSING_XATO"
    HTTP_KODI = 503
    OGIRLIK = Ogirlik.ORTA
    QAYTA_URINISH = True
    FOYDALANUVCHIGA = "To'lov tizimi vaqtincha mavjud emas"
    JURNAL_DARAJASI = logging.WARNING


class XavfsizlikXatosi(TolovXatosi):
    """Firibgarlik va xavfsizlik."""
    KOD = "XAVFSIZLIK"
    HTTP_KODI = 403
    OGIRLIK = Ogirlik.KRITIK
    FOYDALANUVCHIGA = "Tranzaksiya rad etildi"
    JURNAL_DARAJASI = logging.CRITICAL


# ═══ ANIQ XATOLAR ═══

class KartaRaqamiNotogri(KartaXatosi):
    KOD = "KARTA_RAQAM"
    FOYDALANUVCHIGA = "Karta raqami noto'g'ri"

    def __init__(self, raqam: str, sabab: str = "", **kw):
        self.oxirgi4 = raqam[-4:] if len(raqam) >= 4 else "****"
        super().__init__(
            f"Karta raqami yaroqsiz: ****{self.oxirgi4}"
            + (f" ({sabab})" if sabab else ""),
            oxirgi4=self.oxirgi4, **kw,
        )

    def maslahat(self) -> str:
        return "Karta raqamini qayta tekshiring (16 raqam)"


class KartaMuddatiTugagan(KartaXatosi):
    KOD = "KARTA_MUDDAT"
    FOYDALANUVCHIGA = "Karta muddati tugagan"

    def __init__(self, muddat: str, **kw):
        self.muddat = muddat
        super().__init__(f"Karta muddati tugagan: {muddat}",
                         muddat=muddat, **kw)

    def maslahat(self) -> str:
        return "Boshqa karta ishlating"


class KartaBloklangan(KartaXatosi):
    KOD = "KARTA_BLOK"
    HTTP_KODI = 403
    OGIRLIK = Ogirlik.ORTA
    FOYDALANUVCHIGA = "Karta bloklangan"
    JURNAL_DARAJASI = logging.WARNING

    def maslahat(self) -> str:
        return "Bankingizga murojaat qiling"


class MablagYetarliEmas(MablagXatosi):
    KOD = "MABLAG_YOQ"
    FOYDALANUVCHIGA = "Hisobda mablag' yetarli emas"

    def __init__(self, kerak: Decimal, bor: Decimal,
                 valyuta: str = "UZS", **kw):
        self.kerak, self.bor, self.valyuta = kerak, bor, valyuta
        self.yetishmayapti = kerak - bor
        super().__init__(
            f"Mablag' yetarli emas: {self.yetishmayapti:,.2f} "
            f"{valyuta} kerak",
            kerak=str(kerak), bor=str(bor), **kw,
        )

    def maslahat(self) -> str:
        return (f"Hisobingizni {self.yetishmayapti:,.0f} "
                f"{self.valyuta} ga to'ldiring")


class LimitOshdi(MablagXatosi):
    KOD = "LIMIT"
    FOYDALANUVCHIGA = "Kunlik limit oshdi"

    def __init__(self, limit: Decimal, ishlatilgan: Decimal,
                 tiklash: datetime | None = None, **kw):
        self.limit, self.ishlatilgan = limit, ishlatilgan
        self.tiklash = tiklash or (datetime.now() + timedelta(days=1))
        super().__init__(
            f"Limit oshdi: {ishlatilgan:,.0f}/{limit:,.0f}",
            limit=str(limit), **kw,
        )

    def maslahat(self) -> str:
        return f"Limit {self.tiklash:%d.%m %H:%M} da tiklanadi"


class ProtsessingUlanmadi(ProtsessingXatosi):
    KOD = "PROTS_ULANISH"

    def __init__(self, xizmat: str, urinish: int = 1, **kw):
        self.xizmat, self.urinish = xizmat, urinish
        super().__init__(
            f"Protsessingga ulanib bo'lmadi: {xizmat}",
            xizmat=xizmat, urinish=urinish, **kw,
        )

    def maslahat(self) -> str:
        return "Bir necha daqiqadan keyin urinib ko'ring"


class ProtsessingVaqt(ProtsessingXatosi):
    KOD = "PROTS_VAQT"
    HTTP_KODI = 504

    def __init__(self, sekund: float, **kw):
        self.sekund = sekund
        super().__init__(f"Protsessing javob bermadi ({sekund}s)",
                         sekund=sekund, **kw)


class FiribgarlikShubhasi(XavfsizlikXatosi):
    KOD = "FIRIBGARLIK"

    def __init__(self, sabab: str, ball: int = 0, **kw):
        self.sabab, self.ball = sabab, ball
        super().__init__(
            f"Firibgarlik shubhasi: {sabab} (ball: {ball})",
            ball=ball, **kw,
        )

    def maslahat(self) -> str:
        return "Qo'llab-quvvatlash xizmatiga murojaat qiling"


class TakroriyTolov(XavfsizlikXatosi):
    KOD = "TAKROR"
    HTTP_KODI = 409
    OGIRLIK = Ogirlik.ORTA
    FOYDALANUVCHIGA = "Bu to'lov allaqachon amalga oshirilgan"
    JURNAL_DARAJASI = logging.WARNING

    def __init__(self, asl_id: str, **kw):
        self.asl_id = asl_id
        super().__init__(f"Takroriy to'lov: {asl_id}",
                         asl_id=asl_id, **kw)

    def maslahat(self) -> str:
        return f"Asl tranzaksiya: {self.asl_id}"


# ═══ IERARXIYA ═══

print("1. Ierarxiya va xossalar:\n")


def daraxt(Sinf, chuqurlik=0):
    q = [(chuqurlik, Sinf)]
    for a in sorted(Sinf.__subclasses__(), key=lambda c: c.__name__):
        q.extend(daraxt(a, chuqurlik + 1))
    return q


ogirlik_sarlavha = "Og'irlik"
print(f"  {'Sinf':<38} {'Kod':<16} {'HTTP':<6} {ogirlik_sarlavha:<9} {'🔄'}")
print("  " + "─" * 80)
for chuqurlik, Sinf in daraxt(TolovXatosi):
    nom = f"{'  ' * chuqurlik}{'└─ ' if chuqurlik else ''}{Sinf.__name__}"
    print(f"  {nom:<38} {Sinf.KOD:<16} {Sinf.HTTP_KODI:<6} "
          f"{str(Sinf.OGIRLIK):<9} "
          f"{'✅' if Sinf.QAYTA_URINISH else ''}")


print("\n\n2. ⭐ Xatolar va javoblar:\n")

XATOLAR = [
    KartaRaqamiNotogri("4242424242424241", "Luhn tekshiruvi",
                       tranzaksiya_id="TX-001"),
    KartaMuddatiTugagan("01/24", tranzaksiya_id="TX-002"),
    KartaBloklangan(tranzaksiya_id="TX-003"),
    MablagYetarliEmas(Decimal("1500000"), Decimal("320000"),
                      tranzaksiya_id="TX-004"),
    LimitOshdi(Decimal("5000000"), Decimal("5200000"),
               tranzaksiya_id="TX-005"),
    ProtsessingUlanmadi("uzcard-api", urinish=3,
                        tranzaksiya_id="TX-006"),
    ProtsessingVaqt(30.0, tranzaksiya_id="TX-007"),
    FiribgarlikShubhasi("Noodatiy geolokatsiya", ball=87,
                        tranzaksiya_id="TX-008"),
    TakroriyTolov("TX-000", tranzaksiya_id="TX-009"),
]

for e in XATOLAR:
    kod, tana = e.javob()
    print(f"  {type(e).__name__}:")
    print(f"    Texnik:        {e}")
    print(f"    HTTP {kod}:      {tana['xato']['xabar']}")
    if "maslahat" in tana["xato"]:
        print(f"    💡 Maslahat:   {tana['xato']['maslahat']}")
    print()


print("3. ⭐ Toifalar bo'yicha ushlash:\n")

TOIFALAR = [
    (KartaXatosi, "Karta muammosi"),
    (MablagXatosi, "Mablag' muammosi"),
    (ProtsessingXatosi, "Tizim muammosi"),
    (XavfsizlikXatosi, "Xavfsizlik"),
]

for Toifa, tavsif in TOIFALAR:
    mos = [e for e in XATOLAR if isinstance(e, Toifa)]
    print(f"  except {Toifa.__name__:<20} → {tavsif}")
    print(f"    {len(mos)} xato: "
          f"{[type(e).__name__ for e in mos]}")
    print()


print("4. Og'irlik bo'yicha:\n")

ogirlik_guruh: dict[str, list] = {}
for e in XATOLAR:
    ogirlik_guruh.setdefault(str(e.OGIRLIK), []).append(e)

for og in ("past", "orta", "yuqori", "kritik"):
    ro = ogirlik_guruh.get(og, [])
    if not ro:
        continue
    belgi = {"past": "ℹ️", "orta": "⚠️", "yuqori": "🔴",
             "kritik": "🚨"}[og]
    print(f"  {belgi} {og.upper():<8} ({len(ro)}):")
    for e in ro:
        print(f"      {type(e).__name__:<24} {e.FOYDALANUVCHIGA}")
    print()


print("5. ⭐ Jurnal darajalari:\n")

for e in XATOLAR:
    e.jurnalga()

print(f"  Jurnal ({len(_b.getvalue().rstrip().split(chr(10)))} qator):")
for q in _b.getvalue().rstrip().split("\n"):
    print(f"    {q[:76]}")


print("\n\n6. ⭐ Qayta urinish mantiqi:\n")


def tolov_urin(xato_yasovchi, maks_urinish: int = 3):
    """⭐ Faqat QAYTA_URINISH=True bo'lganda."""
    izlar = []
    for urinish in range(1, maks_urinish + 1):
        try:
            e = xato_yasovchi(urinish)
            if e is None:
                izlar.append(f"✅ {urinish}-urinishda muvaffaqiyat")
                return izlar
            raise e
        except TolovXatosi as e:
            if not e.QAYTA_URINISH:
                izlar.append(f"❌ {type(e).__name__} — "
                             f"qayta urinish FOYDASIZ")
                return izlar
            izlar.append(f"🔄 {urinish}-urinish: {type(e).__name__}")
    izlar.append(f"❌ {maks_urinish} urinish muvaffaqiyatsiz")
    return izlar


SENARIYLAR = [
    ("Vaqtinchalik (3-urinishda tuzaladi)",
     lambda n: None if n >= 3 else ProtsessingUlanmadi("api", n)),
    ("Doimiy tarmoq xatosi",
     lambda n: ProtsessingVaqt(30.0)),
    ("Karta xatosi (qayta urinilmaydi)",
     lambda n: KartaMuddatiTugagan("01/24")),
    ("Mablag' yetarli emas",
     lambda n: MablagYetarliEmas(Decimal("1000"), Decimal("100"))),
]

for tavsif, f in SENARIYLAR:
    print(f"  {tavsif}:")
    for q in tolov_urin(f):
        print(f"    {q}")
    print()


print("7. ⭐ Serializatsiya (sirlar bilan/siz):\n")

e = FiribgarlikShubhasi("Noodatiy geolokatsiya: Nigeria", ball=87,
                        tranzaksiya_id="TX-008",
                        ip="41.203.x.x", karta="****4242")

print(f"  Foydalanuvchiga (sirlarsiz):")
print(f"    {json.dumps(e.lugat(sirlar=False), ensure_ascii=False)}")

print(f"\n  Jurnalga (sirlar bilan):")
print(f"    {json.dumps(e.lugat(sirlar=True), ensure_ascii=False)[:120]}...")

print(f"\n  HTTP javob:")
kod, tana = e.javob()
print(f"    HTTP {kod}")
print(f"    {json.dumps(tana, ensure_ascii=False, indent=6)}")

print(f"""

  ⭐ BU LOYIHADA:

     ASOS SINF (TolovXatosi):
       • ClassVar sozlamalar: KOD, HTTP_KODI, OGIRLIK,
         QAYTA_URINISH, FOYDALANUVCHIGA, JURNAL_DARAJASI
       • javob() — HTTP javobi (sirlarsiz)
       • jurnalga() — to'g'ri darajada
       • lugat(sirlar=) — ikki ko'rinish
       • maslahat() — avlodlar qayta aniqlaydi

     TOIFALAR (4 ta):
       Karta / Mablag' / Protsessing / Xavfsizlik
       → foydalanuvchi kerakli darajani tanlaydi

     ANIQ XATOLAR (9 ta):
       Har biri o'z atributlari va maslahati bilan

  ⭐ NATIJA:
     • Foydalanuvchi: tushunarli xabar + maslahat
     • Frontend: HTTP kodi + xato kodi
     • Dasturchi: to'liq kontekst jurnalda
     • Monitoring: og'irlik bo'yicha ogohlantirish
     • Qayta urinish: avtomatik va to'g'ri
""")

Natijaning muhim qismi:

text
1. Ierarxiya va xossalar:

  Sinf                                   Kod              HTTP   Og'irlik  🔄
  ────────────────────────────────────────────────────────────────────────────
  TolovXatosi                            TOLOV_XATO       500    yuqori
    └─ KartaXatosi                       KARTA_XATO       400    past
      └─ KartaBloklangan                 KARTA_BLOK       403    orta
      └─ KartaMuddatiTugagan             KARTA_MUDDAT     400    past
    └─ ProtsessingXatosi                 PROTSESSING_XATO 503    orta      ✅
      └─ ProtsessingUlanmadi             PROTS_ULANISH    503    orta      ✅

2. ⭐ Xatolar va javoblar:

  MablagYetarliEmas:
    Texnik:        [MABLAG_YOQ] Mablag' yetarli emas: 1,180,000.00 UZS kerak | tx=TX-004 | kerak='1500000', bor='320000'
    HTTP 402:      Hisobda mablag' yetarli emas
    💡 Maslahat:   Hisobingizni 1,180,000 UZS ga to'ldiring

6. ⭐ Qayta urinish mantiqi:

  Vaqtinchalik (3-urinishda tuzaladi):
    🔄 1-urinish: ProtsessingUlanmadi
    🔄 2-urinish: ProtsessingUlanmadi
    ✅ 3-urinishda muvaffaqiyat

  Karta xatosi (qayta urinilmaydi):
    ❌ KartaMuddatiTugagan — qayta urinish FOYDASIZ

Nima ko'rsatdi: 2.2, 2.4, 2.6-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Har funksiya uchun o'z istisnosi" Ajratilmasa — ortiqcha
"pass yetadi" Docstring yozing
"super().__init__() ixtiyoriy" str(e) xom kortej yoki bo'sh qator bo'ladi
"args ahamiyatsiz" pickle va repr unga tayanadi
"KeyError dan meros — yaxshi" str(e) tirnoq bilan
"BaseException — asos sinf" Hech qachon
"Istisno konstruktorida jurnal" Yon ta'sir
"Xabarni tahlil qilish normal" Atributlar ishlating

6. Keng tarqalgan xatolar va yechimlari

1. super().__init__() unutish

python
def __init__(self, kerak, bor): ...         # ⚠️ str(e) = '(1000, 300)'
    super().__init__(f"...")                # ✅ o'qiladigan xabar

2. args konstruktorga mos emas

python
def __init__(self, a, b):
    super().__init__(f"{a}-{b}")            # ⚠️ pickle buziladi
    super().__init__(a, b)                  # ✅ + __str__

3. BaseException dan meros

python
class X(BaseException): ...                 # ❌
class X(Exception): ...                     # ✅

4. KeyError dan meros

python
class X(KeyError): ...                      # ⚠️ str(e) tirnoq bilan
    def __str__(self): return self.args[0]  # ✅

5. Yon ta'sir konstruktorda

python
def __init__(self, m):
    jurnal.error(m)                         # ⚠️
    super().__init__(m)

6. Umumiy asos yo'q

python
class XatoA(Exception): ...                 # ⚠️ bog'lanmagan
class KutubxonaXatosi(Exception): ...       # ✅ asos
class XatoA(KutubxonaXatosi): ...

7. Xabarni tahlil qilish

python
if "503" in str(e): ...                     # ⚠️ mo'rt
if e.kod == 503: ...                        # ✅

8. O'zgaruvchan sukut

python
def __init__(self, teglar=[]): ...          # ⚠️
def __init__(self, teglar=None):
    self.teglar = list(teglar or [])        # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 9.5-dars (o'tilgan): raise ... from e
  • 9.7-dars: ierarxiya loyihalash — davomi
  • 8-qism: sinflar, meros, __str__, ClassVar
  • 17-qism: pytest.raises bilan testlash
  • 20-qism: API xatolari, HTTP kodlari
  • requests, sqlalchemy, httpx — namunali ierarxiyalar

8. Eng yaxshi amaliyotlar

  1. Kutubxona uchun bitta asos sinf. Foydalanuvchi hammasini ushlashi mumkin.

  2. Exception dan meros oling. O'rnatilgan turlardan — faqat asosli sabab bilan.

  3. super().__init__() doim chaqiring. args konstruktorga mos bo'lsin.

  4. Atributlar bering. Xabarni tahlil qilish — antipattern.

  5. Docstring yozing. pass emas.

  6. Konstruktorda yon ta'sir yo'q. Faqat ma'lumot saqlash.

  7. ClassVar sozlamalar. HTTP kodi, og'irlik, qayta urinish.

  8. Ikki ko'rinish. Foydalanuvchi uchun qisqa, jurnal uchun to'liq.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  class X(Exception): pass
    print(str(X("a")), X("a").args)
2.  class X(Exception):
        def __init__(self, a, b): self.a, self.b = a, b
    print(repr(str(X(1, 2))))
3.  class X(KeyError): pass
    print(str(X("xabar")))
4.  class X(Exception):
        def __init__(self, a, b):
            super().__init__(f"{a}+{b}")
    import pickle
    try: pickle.loads(pickle.dumps(X(1, 2)))
    except TypeError: print("TypeError")
5.  class X(BaseException): pass
    try: raise X()
    except Exception: print("ushlandi")
    except BaseException: print("o'tdi")
6.  class X(Exception): pass
    print(X.__mro__[:3])
7.  class X(Exception): pass
    print(repr(X("a", "b")))
8.  class X(Exception):
        def __str__(self): return "maxsus"
    print(str(X("a")), X("a").args)
9.  class X(ValueError): pass
    try: raise X("a")
    except ValueError as e: print(type(e).__name__)
10. class X(Exception): pass
    e = X()
    print(str(e), e.args)
11. class X(Exception):
        KOD = "E1"
    class Y(X):
        KOD = "E2"
    print(X.KOD, Y.KOD, Y("a").KOD)
12. class X(Exception): pass
    print(isinstance(X("a"), Exception), issubclass(X, BaseException))
Javoblar
  1. a ('a',)
  2. '(1, 2)' — super().__init__() yo'q, lekin BaseException.__new__ pozitsion argumentlarni args ga baribir yozgan; str(e) shu xom kortejni ko'rsatadi
  3. 'xabar' — tirnoq bilan
  4. TypeError — args 1 ta, konstruktor 2 ta
  5. o'tdi — BaseException
  6. (<class '__main__.X'>, <class 'Exception'>, <class 'BaseException'>) — skript sifatida ishga tushirilganda modul nomi __main__
  7. X('a', 'b')
  8. maxsus ('a',)
  9. X
  10. '' ()
  11. E1 E2 E2
  12. True True

Vazifa 2: Xatolarni tuzating

python
1.  class X(Exception):
        def __init__(self, a, b): self.a, self.b = a, b
2.  class X(BaseException): pass
3.  class X(KeyError): pass
4.  class X(Exception):
        def __init__(self, m):
            jurnal.error(m)
            super().__init__(m)
5.  class X(Exception):
        def __init__(self, teglar=[]): self.teglar = teglar
6.  class YuklashXatosi(Exception): pass
    class SaqlashXatosi(Exception): pass
7.  if "timeout" in str(e): qayta_urin()
8.  class X(Exception): pass       # docstring yo'q
Javoblar
python
1.  super().__init__(a, b) + __str__
2.  class X(Exception)
3.  __str__ ni qayta aniqlang yoki Exception dan meros
4.  jurnal ni except blokiga ko'chiring
5.  teglar=None; self.teglar = list(teglar or [])
6.  Umumiy asos: class QuvurXatosi(Exception)
7.  if isinstance(e, VaqtTugadi): yoki if e.kod == 408:
8.  """Nima uchun kerakligi."""

Vazifa 3: Kutubxona istisnolari

Yozing:

  1. Asos sinf + 3 toifa + 8 aniq xato
  2. Har birida ClassVar sozlamalar
  3. Atributlar va maslahat() metodi
  4. javob() — HTTP javobi
  5. lugat(sirlar=) — ikki ko'rinish
  6. __all__ bilan ommaviy API

Vazifa 4: Istisno tekshiruvchisi

Vosita yozing (AST bilan):

  1. Exception avlodlarini topsin
  2. super().__init__() yo'qligini
  3. BaseException dan merosni
  4. Konstruktorda yon ta'sirni
  5. Docstring yo'qligini
  6. Bog'lanmagan sinflarni (umumiy asos yo'q)

Vazifa 5: Validatsiya istisnosi

  1. MaydonXatosi dataclass
  2. ValidatsiyaXatosi — bir nechta maydon
  3. __iter__, __len__, maydonlar()
  4. Ichma-ich yo'llar (manzil.shahar)
  5. Frontend uchun format
  6. ExceptionGroup bilan integratsiya (3.11+)

Vazifa 6: Xato kodlari tizimi

  1. XatoKodi enum (E1001 formatda)
  2. Har istisnoda kod
  3. Hujjat URL generatsiyasi
  4. Ko'p tilli xabarlar
  5. Kod bo'yicha qidiruv
  6. Markdown hujjat generatori

Vazifa 7: O'ylash

Nega Pythonda Exception va BaseException ikkita asos sinf — bitta yetmasmidi?

Javob

Chunki uch istisno turi — SystemExit, KeyboardInterrupt, GeneratorExit — dastur oqimini boshqaradi, xato emas.

1. Muammo: except Exception: nima ushlashi kerak

python
while True:
    try:
        ish()
    except Exception:
        jurnal.exception("Xato")
        continue                    # ⭐ davom etamiz

Agar Ctrl+C ham Exception bo'lsa:

python
# Foydalanuvchi Ctrl+C bosdi
# → KeyboardInterrupt
# → except Exception uni ushlaydi
# → continue
# → ⚠️ CHEKSIZ TSIKL — dasturni to'xtatib bo'lmaydi

Shuning uchun KeyboardInterrupt Exception emas.

2. Python 2.4 gacha — bitta ierarxiya

python
# Python 2.4 va oldin:
Exception
 ├── SystemExit                     ⚠️ Exception avlodi
 ├── KeyboardInterrupt              ⚠️
 └── StandardError
      ├── ValueError
      └── ...

Muammo:

python
try:
    ish()
except Exception:                   # ⚠️ Ctrl+C ni ham ushlardi
    ...

Shuning uchun dasturchilar yozardi:

python
except (KeyboardInterrupt, SystemExit):
    raise                           # ⚠️ har joyda takror
except Exception:
    ...

3. PEP 352 (2005) — qayta qurish

Brett Cannon taklif qildi:

BaseException                       ⭐ YANGI asos
 ├── SystemExit
 ├── KeyboardInterrupt
 ├── GeneratorExit                  (2.5 da qo'shildi)
 └── Exception                      ⭐ "oddiy" xatolar
      ├── ValueError
      └── ...

Natija:

python
except Exception:                   # ✅ Ctrl+C o'tadi

Python 3 da StandardError ham olib tashlandi — u ortiqcha edi.

4. Uch maxsus istisno

a) SystemExit — sys.exit():

python
import sys

def main():
    if xato:
        sys.exit(1)                 # ⭐ SystemExit ko'taradi

try:
    main()
except Exception:                   # ✅ o'tadi — dastur chiqadi
    ...

sys.exit() — bu istisno, chunki finally bloklar bajarilishi kerak:

python
try:
    sys.exit(1)
finally:
    tozala()                        # ⭐ bajariladi

b) KeyboardInterrupt — Ctrl+C:

Foydalanuvchi dasturni to'xtatmoqchi — dastur buni hurmat qilishi kerak.

c) GeneratorExit — generator yopilishi:

python
def gen():
    try:
        yield 1
        yield 2
    except GeneratorExit:           # ⭐ close() chaqirilganda
        tozala()
        raise                       # ⚠️ yutmaslik kerak

g = gen()
next(g)
g.close()                           # ⭐ GeneratorExit yuboradi

Agar generator GeneratorExit ni yutsa:

python
def yomon():
    try:
        yield 1
    except GeneratorExit:
        yield 2                     # ❌ RuntimeError: generator ignored

5. Amaliy oqibatlar

python
# ⭐ 1. except Exception — xavfsiz
try:
    ish()
except Exception:
    jurnal.exception("Xato")
    # ⭐ Ctrl+C va sys.exit() O'TADI

# ⚠️ 2. except: — xavfli
try:
    ish()
except:                             # ⚠️ BaseException
    ...

# ⭐ 3. Ataylab hammasini ushlash
try:
    ish()
except BaseException:               # ✅ ANIQ niyat
    tozala()
    raise                           # ⭐ qayta ko'tarish SHART

# ⭐ 4. Faqat oqim boshqaruvini o'tkazish
try:
    ish()
except (KeyboardInterrupt, SystemExit):
    raise
except Exception:
    ...
# ⚠️ Bu ORTIQCHA — except Exception allaqachon o'tkazadi

6. Boshqa tillar

Til Ierarxiya
Python BaseException → Exception
Java Throwable → Error / Exception
C# Exception (bitta)
JavaScript Error (bitta)
Ruby Exception → StandardError

Java — juda o'xshash:

java
Throwable
 ├── Error                          ⭐ OutOfMemoryError, StackOverflowError
 │                                     (tuzatib bo'lmaydi)
 └── Exception
      ├── RuntimeException          (unchecked)
      └── IOException, ...          (checked)
java
try {
    ish();
} catch (Exception e) {             // ⭐ Error o'tadi
    ...
}

Ruby ham:

ruby
Exception
 ├── SystemExit
 ├── Interrupt                      # Ctrl+C
 ├── NoMemoryError
 └── StandardError                  # ⭐ odatiy xatolar
      ├── ArgumentError
      └── ...

begin
  ish
rescue                              # ⭐ SUKUT: StandardError
  ...
end

Ruby eng yaxshi: rescue (turi ko'rsatilmasa) sukut bo'yicha StandardError ni ushlaydi — ya'ni Ctrl+C avtomatik o'tadi.

Python da except: — BaseException. Aksincha bo'lgani yaxshiroq bo'lardi, lekin orqaga moslik.

7. MemoryError va SystemError nega Exception

Qiziq savol:

python
issubclass(MemoryError, Exception)          # True
issubclass(SystemError, Exception)          # True
issubclass(RecursionError, Exception)       # True

Bular ham "tuzatib bo'lmaydigan" xatolar. Nega BaseException emas?

Sabab: ular ba'zan tuzatilishi mumkin:

python
try:
    katta = [0] * 10**12
except MemoryError:
    katta = None                    # ⭐ kichikroq bilan davom etamiz

Java da OutOfMemoryError — Error (ushlanmaydi), Python da esa Exception.

8. except* va BaseExceptionGroup

3.11 da bu farq yana muhim bo'ldi:

python
ExceptionGroup("x", [ValueError()])                 # ✅
ExceptionGroup("x", [KeyboardInterrupt()])          # ❌ TypeError
BaseExceptionGroup("x", [KeyboardInterrupt()])      # ✅

ExceptionGroup faqat Exception avlodlarini qabul qiladi.

9. O'z istisnongiz uchun

python
# ⭐ 99.9% holatda
class MeningXatom(Exception): ...

# ⚠️ BaseException — faqat agar:
#   • Dastur oqimini to'xtatishi kerak
#   • except Exception: uni USHLAMASLIGI kerak
#   • Framework ichki mexanizmi

# Misol (juda kamdan-kam):
class TestSkipped(BaseException):
    """⚠️ Test frameworkida — test kodi uni ushlamasin."""

Bu nazariy misol emas: pytest'ning o'zida pytest.skip() va pytest.fail() ko'taradigan istisnolar OutcomeException(BaseException) dan meros oladi. Sabab aynan shu: test ichidagi except Exception: bloki "testni o'tkazib yubor" signalini tasodifan yutib yubormasligi kerak. Bu — BaseException dan meros olish oqlanadigan kam uchraydigan holatlardan biri: istisno xato emas, boshqaruv oqimi signali.

10. Xulosa

Ikki asos sinf kerak, chekki:

  1. except Exception: xavfsiz bo'lishi kerak — Ctrl+C va sys.exit() o'tishi shart
  2. Uch istisno — oqim boshqaruvi, xato emas: SystemExit, KeyboardInterrupt, GeneratorExit
  3. finally bloklar bajarilishi kerak — shuning uchun sys.exit() istisno
  4. Java va Ruby ham shunday — bu umumiy dizayn yechimi

Yagona afsus: except: — BaseException ni ushlaydi. Ruby dagidek Exception (ya'ni StandardError) bo'lgani yaxshiroq bo'lardi.

Amaliy qoida:

python
class MeningXatom(Exception): ...   # ⭐ har doim
except Exception:                    # ⭐ yuqori darajada
except:                              # ❌ hech qachon
except BaseException:                # ⚠️ faqat raise bilan

Nimani mustahkamlaydi: 2.3, 2.7-bo'limlar.


Xulosa

Bu darsda o'z istisnolaringizni yaratishni o'rgandik.

Eng muhim uch fikr:

  1. Kutubxona uchun bitta asos sinf va toifalar ierarxiyasi. KutubxonaXatosi → TarmoqXatosi → VaqtTugadi — foydalanuvchi kerakli darajani tanlaydi: hammasi, toifa yoki aniq xato. Asos sinf Exception dan meros olsin; BaseException — hech qachon, chunki except Exception: uni ushlamaydi.

  2. super().__init__() chaqirish majburiy va args konstruktorga mos bo'lsin. Unutilsa str(e) o'qiladigan xabar o'rniga xom kortej ('(1000, 300)') yoki, faqat nomli argumentlarda, bo'sh qator bo'ladi; args mos kelmasa pickle buziladi (u istisnoni Sinf(*e.args) bilan qayta yaratadi). Yechim: super().__init__(a, b) + o'z __str__ metodi, yoki __reduce__.

  3. Atributlar bering — xabarni tahlil qilish antipattern. if "503" in str(e) mo'rt; if e.kod == 503 ishonchli. ClassVar sozlamalar (HTTP_KODI, QAYTA_URINISH, OGIRLIK) har avlodga o'z xatti-harakatini beradi, va istisno o'zi biladi: qaysi HTTP kodi, qayta urinish kerakmi, foydalanuvchiga nima deyish kerak.

Keyingi darsda istisnolar ierarxiyasini loyihalashni chuqurroq ko'ramiz: standart kutubxona ierarxiyasi, mashhur kutubxonalar namunalari va migratsiya strategiyalari.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
9.6-dars: O'z istisnolaringiz — IlmHamroh