IlmHamroh
Python kursi/OOP17/20-dars42 daqiqa
Mundarija (22)

8.17-dars: dataclass

8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 17-dars


1. Kirish va motivatsiya

Uch atributli oddiy sinf — 25 qator:

python
class Nuqta:
    def __init__(self, x, y, z=0):
        self.x, self.y, self.z = x, y, z

    def __repr__(self):
        return f"Nuqta(x={self.x!r}, y={self.y!r}, z={self.z!r})"

    def __eq__(self, b):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.x, self.y, self.z) == (b.x, b.y, b.z)

    def __hash__(self):
        return hash((self.x, self.y, self.z))

    def __lt__(self, b): ...
    def __le__(self, b): ...
    def __gt__(self, b): ...
    def __ge__(self, b): ...

dataclass bilan — uch qator:

python
from dataclasses import dataclass

@dataclass(order=True, frozen=True)
class Nuqta:
    x: float
    y: float
    z: float = 0.0

Va bu tuzoqni taqiqlaydi:

python
@dataclass
class A:
    teglar: list = []           # ❌ ValueError yaratish paytida!

Bu darsda:

  • @dataclass parametrlari va nima yaratadi
  • field() — har maydonni nozik sozlash
  • __post_init__ va InitVar
  • frozen, slots, kw_only, order
  • Meros, sukut qiymatlar tartibi
  • asdict, astuple, replace, fields
  • Qachon dataclass, qachon NamedTuple, qachon oddiy sinf

2. Nazariya — chuqur tushuntirish

2.1. Nima yaratiladi

python
@dataclass
class Nuqta:
    x: float
    y: float = 0.0

Python quyidagini yozadi:

python
def __init__(self, x: float, y: float = 0.0) -> None:
    self.x = x
    self.y = y

def __repr__(self) -> str:
    return f"Nuqta(x={self.x!r}, y={self.y!r})"

def __eq__(self, other):
    if other.__class__ is self.__class__:
        return (self.x, self.y) == (other.x, other.y)
    return NotImplemented

Tur ko'rsatkichi majburiy:

python
@dataclass
class A:
    x: int              # ✅ maydon
    y = 5               # ⚠️ oddiy sinf atributi, MAYDON EMAS
    z: int = 10         # ✅ maydon, sukut bilan

y __init__ ga kirmaydi.

typing.ClassVar — ataylab maydon emas:

python
from typing import ClassVar

@dataclass
class A:
    JAMI: ClassVar[int] = 0     # ⭐ sinf atributi, maydon emas
    x: int

2.2. Dekorator parametrlari

python
@dataclass(
    init=True,          # __init__
    repr=True,          # __repr__
    eq=True,            # __eq__
    order=False,        # __lt__, __le__, __gt__, __ge__
    unsafe_hash=False,  # __hash__ majburan
    frozen=False,       # o'zgarmas
    match_args=True,    # __match_args__ (3.10+)
    kw_only=False,      # barcha maydonlar nomli (3.10+)
    slots=False,        # __slots__ (3.10+)
    weakref_slot=False, # zaif havola sloti (3.11+)
)

__hash__ qoidalari:

eq frozen __hash__
True (sukut) False (sukut) None hashable emas
True True Avtomatik
False har qanday object.__hash__ (id bo'yicha)
unsafe_hash=True — Majburan

order=True:

python
@dataclass(order=True)
class A:
    x: int
    y: int

A(1, 2) < A(1, 3)               # True — tuple taqqoslash

Maydonlar tartibi taqqoslash tartibini belgilaydi.

2.3. field()

python
from dataclasses import field

@dataclass
class A:
    a: int
    b: list = field(default_factory=list)        # ⭐ o'zgaruvchan sukut
    c: str = field(default="x", repr=False)      # repr da yo'q
    d: int = field(compare=False, default=0)     # taqqoslashda yo'q
    e: int = field(init=False, default=0)        # __init__ da yo'q
    f: dict = field(default_factory=dict,
                    metadata={"birlik": "kg"})   # metama'lumot
    g: int = field(kw_only=True, default=0)      # faqat nomli (3.10+)
    h: int = field(hash=False, default=0)        # __hash__ da yo'q
Parametr Ma'nosi
default Sukut qiymat
default_factory Har nusxa uchun yangi obyekt
init __init__ ga kirsinmi
repr __repr__ da ko'rinsinmi
compare __eq__/__lt__ da hisobga olinsinmi
hash __hash__ da (sukut: compare qiymati)
metadata Ixtiyoriy metama'lumot (MappingProxyType)
kw_only Faqat nomli argument (3.10+)

default va default_factory birga bo'lmaydi.

2.4. O'zgaruvchan sukut tuzog'i

python
@dataclass
class A:
    teglar: list = []           # ❌ ValueError!
ValueError: mutable default <class 'list'> for field teglar
            is not allowed: use default_factory

Bu — dataclass ning eng katta afzalligi. Oddiy sinfda:

python
class A:
    def __init__(self, teglar=[]):      # ⚠️ jimgina tuzoq
        self.teglar = teglar

To'g'ri:

python
@dataclass
class A:
    teglar: list[str] = field(default_factory=list)
    sozlama: dict = field(default_factory=dict)
    kesh: set = field(default_factory=set)
    nuqta: "Nuqta" = field(default_factory=lambda: Nuqta(0, 0))

Tekshiruv __hash__ bo'yicha: tuple, frozenset, str ruxsat; list, dict, set — rad etiladi.

2.5. __post_init__ va InitVar

python
@dataclass
class Togriturtburchak:
    en: float
    boy: float
    yuza: float = field(init=False)             # ⭐ hisoblanadi

    def __post_init__(self):
        if self.en <= 0 or self.boy <= 0:
            raise ValueError("Musbat bo'lsin")
        self.yuza = self.en * self.boy

InitVar — faqat __init__ uchun:

python
from dataclasses import InitVar

@dataclass
class Foydalanuvchi:
    ism: str
    parol: InitVar[str]                         # ⭐ maydon EMAS
    parol_xesh: str = field(init=False, default="")

    def __post_init__(self, parol: str):        # ⭐ argument sifatida
        self.parol_xesh = xesh(parol)

InitVar maydonlari __post_init__ ga argument sifatida uzatiladi va obyektda saqlanmaydi.

Tuzoq: InitVar ga standart qiymat berilsa (parol: InitVar[str] = ""), u sinf atributi bo'lib qoladi. Shunda hasattr(obj, "parol") True qaytaradi — lekin bu nusxadagi parol emas, sinfdagi "". Nusxada saqlanmaganini tekshirish uchun "parol" in vars(obj) ishlating.

frozen bilan:

python
@dataclass(frozen=True)
class A:
    x: int
    ikki_x: int = field(init=False)

    def __post_init__(self):
        object.__setattr__(self, "ikki_x", self.x * 2)   # ⭐ maxsus

2.6. Meros va sukut qiymatlar

python
@dataclass
class Ota:
    a: int
    b: int = 0

@dataclass
class Bola(Ota):
    c: int                      # ❌ TypeError!
TypeError: non-default argument 'c' follows default argument

Sabab: yaratilgan __init__(self, a, b=0, c) — noto'g'ri.

Yechimlar:

python
# 1. Sukut qiymat berish
@dataclass
class Bola(Ota):
    c: int = 0

# 2. kw_only (3.10+)
@dataclass(kw_only=True)
class Ota:
    a: int
    b: int = 0

@dataclass(kw_only=True)
class Bola(Ota):
    c: int                      # ✅ ishlaydi

# 3. Faqat shu maydon
@dataclass
class Bola(Ota):
    c: int = field(kw_only=True)

Maydon tartibi merosda:

python
@dataclass
class Ota:
    a: int
    b: int = 0

@dataclass
class Bola(Ota):
    b: int = 5                  # ⭐ qayta aniqlash — TARTIB saqlanadi
    c: int = 10

# __init__(self, a, b=5, c=10)  — b hali ham ikkinchi

2.7. Yordamchi funksiyalar

python
from dataclasses import (asdict, astuple, replace, fields,
                         is_dataclass, MISSING)

asdict(obj)                     # rekursiv lug'at
astuple(obj)                    # rekursiv tuple
replace(obj, x=5)               # ⭐ nusxa + o'zgarish
fields(obj)                     # Field obyektlari
is_dataclass(obj)               # tekshirish

asdict — chuqur nusxa:

python
@dataclass
class A:
    lst: list

a = A([1, 2])
d = asdict(a)
d["lst"] is a.lst               # False ⭐ nusxa

replace — o'zgarmas obyektlar uchun:

python
@dataclass(frozen=True)
class Sozlama:
    host: str
    port: int

s = Sozlama("localhost", 8000)
s2 = replace(s, port=443)       # ✅ yangi obyekt

init=False maydonlarni replace ga uzatib bo'lmaydi.

2.8. Qachon nima

dataclass NamedTuple TypedDict Oddiy sinf pydantic
O'zgaruvchan
frozen (doim) qo'lda
Tur tekshiruvi qo'lda
Indeks (x[0])
Ochish (unpack)
Meros
Xotira o'rta kam kam o'rta ko'p
Bog'liqlik

Tanlash:

  • NamedTuple — o'zgarmas, tuple kabi, funksiyadan bir necha qiymat
  • dataclass — o'zgaruvchan yoki frozen, metodlar bilan
  • TypedDict — JSON/lug'at strukturasi (obyekt emas)
  • pydantic — tashqi ma'lumot, ish vaqtida tekshiruv
  • Oddiy sinf — argumentlar maydonlarga mos kelmasa

3. Tez ma'lumotnoma

Asosiy

python
from dataclasses import dataclass, field

@dataclass(order=True, frozen=True, slots=True, kw_only=True)
class A:
    x: int                                   maydon
    y: list = field(default_factory=list)    ⭐ o'zgaruvchan
    z: int = field(init=False, default=0)
    JAMI: ClassVar[int] = 0                  ⚠️ maydon EMAS
    w = 5                                    ⚠️ maydon EMAS (tur yo'q)

Parametrlar

init repr eq        sukut True
order               __lt__ va boshqalar (eq=True kerak)
frozen              o'zgarmas
unsafe_hash         __hash__ majburan
slots               __slots__ (3.10+)
kw_only             barcha maydon nomli (3.10+)
match_args          __match_args__ (3.10+)

__hash__: eq=True + frozen=True → avtomatik
          eq=True + frozen=False → None ⚠️

field()

default             sukut qiymat
default_factory     ⭐ har nusxaga yangi
init repr compare   True/False
hash                sukut: compare
metadata            dict
kw_only             3.10+

Yordamchilar

python
asdict(o)       rekursiv lug'at (chuqur nusxa)
astuple(o)      rekursiv tuple
replace(o, x=5) ⭐ nusxa + o'zgarish
fields(o)       Field obyektlari
is_dataclass(o) tekshirish

Meros

Sukutsiz maydon sukutlidan KEYIN → TypeError
→ kw_only=True yoki sukut bering

4. Batafsil misollar

Misol 1 — Nima yaratiladi

python
"""@dataclass qanday kod yozadi."""

from dataclasses import dataclass, field, fields, asdict, astuple
from typing import ClassVar
import inspect

print("=== 1. Yaratilgan metodlar ===\n")


@dataclass
class Nuqta:
    x: float
    y: float = 0.0


print(f"  @dataclass yaratgan metodlar:")
for m in ["__init__", "__repr__", "__eq__", "__hash__", "__lt__"]:
    bor = m in Nuqta.__dict__
    q = Nuqta.__dict__.get(m, "—")
    print(f"    {m:<12} {'✅' if bor else '❌':<4} {q if bor else ''}")

print(f"\n  __init__ imzosi:")
print(f"    {inspect.signature(Nuqta.__init__)}")

n = Nuqta(3, 4)
print(f"\n  Ishlatish:")
print(f"    Nuqta(3, 4)          → {n!r}")
print(f"    Nuqta(3) == Nuqta(3) → {Nuqta(3) == Nuqta(3)}")
print(f"    hash(n)              → ", end="")
try:
    print(hash(n))
except TypeError as e:
    print(f"❌ TypeError: {e}")


print("\n\n=== ⚠️ 2. Tur ko'rsatkichi MAJBURIY ===\n")


@dataclass
class Aralash:
    maydon: int                                 # ✅ maydon
    sukutli: str = "sukut"                      # ✅ maydon
    oddiy = 42                                  # ⚠️ MAYDON EMAS
    KONSTANTA: ClassVar[int] = 100              # ⚠️ ataylab maydon emas
    _ichki: bool = True                         # ✅ maydon


print(f"  Sinf tanasidagi nomlar:")
print(f"    maydon: int          → maydon ✅")
print(f"    sukutli: str = ...   → maydon ✅")
print(f"    oddiy = 42           → ⚠️ oddiy sinf atributi")
print(f"    KONSTANTA: ClassVar  → ⚠️ ataylab maydon emas")

print(f"\n  fields():")
for f in fields(Aralash):
    print(f"    {f.name:<12} {str(f.type):<24} sukut={f.default!r}")

print(f"\n  __init__ imzosi:")
print(f"    {inspect.signature(Aralash.__init__)}")

a = Aralash(1)
print(f"\n  a = Aralash(1)")
print(f"    a.maydon    = {a.maydon}")
print(f"    a.oddiy     = {a.oddiy}   ← sinf atributi")
print(f"    a.KONSTANTA = {a.KONSTANTA}")
print(f"    a.__dict__  = {a.__dict__}")
print(f"    ⭐ oddiy va KONSTANTA nusxa lug'atida YO'Q")

print(f"""
  ⭐ QOIDA: faqat TUR KO'RSATKICHI bor nomlar — maydon.

     x: int          ✅ maydon
     x = 5           ⚠️ oddiy sinf atributi
     x: ClassVar[int] = 5   ⚠️ ataylab sinf atributi
""")


print("=== 3. Parametrlar jadvali ===\n")


@dataclass
class Sukut:
    x: int = 1


@dataclass(order=True)
class Tartibli:
    x: int = 1


@dataclass(frozen=True)
class Muzlatilgan:
    x: int = 1


@dataclass(order=True, frozen=True)
class Toliq:
    x: int = 1


@dataclass(eq=False)
class EqSiz:
    x: int = 1


@dataclass(unsafe_hash=True)
class UnsafeHash:
    x: int = 1


@dataclass(repr=False)
class ReprSiz:
    x: int = 1


SINFLAR = [Sukut, Tartibli, Muzlatilgan, Toliq, EqSiz, UnsafeHash,
           ReprSiz]

ozgarmas = "o'zgarmas"
print(f"  {'Sinf':<14} {'repr':<8} {'==':<6} {'<':<6} {'hash':<8} "
      f"{ozgarmas}")
print("  " + "─" * 56)
for S in SINFLAR:
    a, b = S(), S()
    r = "✅" if "__repr__" in S.__dict__ else "❌"
    eq = "✅" if a == b else "❌"
    try:
        a < b; lt = "✅"
    except TypeError:
        lt = "❌"
    try:
        hash(a); h = "✅"
    except TypeError:
        h = "❌"
    try:
        a.x = 2; muz = "❌"
    except Exception:
        muz = "✅"
    print(f"  {S.__name__:<14} {r:<8} {eq:<6} {lt:<6} {h:<8} {muz}")

print(f"""
  ⭐ __hash__ QOIDALARI:

     eq=True,  frozen=False  →  __hash__ = None    ⚠️ (sukut)
     eq=True,  frozen=True   →  avtomatik          ✅
     eq=False                →  object.__hash__ (id)
     unsafe_hash=True        →  majburan yaratiladi

  ⚠️ SABAB: o'zgaruvchan obyekt hashable bo'lsa —
     to'plamda topilmay qoladi (8.12-dars).
""")


print("=== ⭐ 4. field() parametrlari ===\n")


@dataclass
class Toliq2:
    # Oddiy maydon
    ism: str

    # O'zgaruvchan sukut
    teglar: list[str] = field(default_factory=list)

    # repr da yo'q (sirlar)
    parol: str = field(default="", repr=False)

    # Taqqoslashda yo'q
    yaratilgan: float = field(default=0.0, compare=False)

    # __init__ da yo'q — hisoblanadi
    uzunlik: int = field(init=False, default=0)

    # Metama'lumot
    ogirlik: float = field(default=0.0,
                           metadata={"birlik": "kg", "min": 0})

    # Hash da yo'q
    kesh: dict = field(default_factory=dict, compare=False,
                       repr=False)

    def __post_init__(self):
        self.uzunlik = len(self.ism)


t = Toliq2("Aziz", ["admin"], "maxfiy123", 1234.5, ogirlik=75.5)

print(f"  repr: {t!r}")
print(f"    ⭐ parol va kesh KO'RINMAYDI\n")

print(f"  {'Maydon':<14} {'init':<7} {'repr':<7} {'compare':<9} "
      f"{'Qiymat'}")
print("  " + "─" * 62)
for f in fields(Toliq2):
    q = getattr(t, f.name)
    korinish = repr(q)[:20]
    print(f"  {f.name:<14} {f.init!s:<7} {f.repr!s:<7} "
          f"{f.compare!s:<9} {korinish}")

print(f"\n  Metama'lumot:")
for f in fields(Toliq2):
    if f.metadata:
        print(f"    {f.name}: {dict(f.metadata)}")

print(f"\n  ⭐ compare=False ta'siri:")
t2 = Toliq2("Aziz", ["admin"], "boshqa", 9999.9, ogirlik=75.5)
print(f"    t  = Toliq2('Aziz', ..., yaratilgan=1234.5)")
print(f"    t2 = Toliq2('Aziz', ..., yaratilgan=9999.9)")
print(f"    t == t2 → {t == t2}   ⭐ yaratilgan hisobga olinmadi")
print(f"    ⚠️ parol taqqoslashda BOR (compare=False emas)")


print("\n\n=== ⭐ 5. O'zgaruvchan sukut tuzog'i ===\n")

print(f"  @dataclass ni O'ZI taqiqlaydi:\n")

for tur, nom in [(list, "list"), (dict, "dict"), (set, "set"),
                 (bytearray, "bytearray")]:
    try:
        @dataclass
        class Xato:
            x: tur = tur()
        n = "⚠️ o'tdi"
    except ValueError as e:
        n = f"✅ ValueError: {str(e)[:52]}"
    print(f"    {nom:<12} = {nom}()  → {n}")

print(f"\n  O'zgarmas turlar ruxsat:")
for q, nom in [((), "tuple"), (frozenset(), "frozenset"),
               ("", "str"), (0, "int"), (None, "None")]:
    try:
        @dataclass
        class Ok:
            x: type(q) = q
        n = "✅ ruxsat"
    except ValueError:
        n = "❌ rad etildi"
    print(f"    {nom:<12} = {q!r:<14} → {n}")

print(f"""
  ⭐ TEKSHIRUV: __hash__ bor-yo'qligi bo'yicha
     (dataclasses.py manbasida):

       if f.default.__class__.__hash__ is None:
           raise ValueError(...)

  ⭐ TO'G'RI YECHIM:
""")


@dataclass
class Togri:
    teglar: list[str] = field(default_factory=list)
    sozlama: dict = field(default_factory=dict)
    kesh: set = field(default_factory=set)
    nuqta: Nuqta = field(default_factory=lambda: Nuqta(0, 0))


a, b = Togri(), Togri()
a.teglar.append("x")
print(f"    a.teglar = {a.teglar}")
print(f"    b.teglar = {b.teglar}   ✅ ajralgan")
print(f"    a.nuqta  = {a.nuqta}, a.nuqta is b.nuqta = "
      f"{a.nuqta is b.nuqta}")


print("\n\n=== 6. Yordamchi funksiyalar ===\n")


@dataclass
class Manzil:
    shahar: str
    kocha: str


@dataclass
class Odam:
    ism: str
    manzil: Manzil
    teglar: list[str] = field(default_factory=list)


o = Odam("Aziz", Manzil("Toshkent", "Amir Temur"), ["admin", "faol"])

print(f"  o = {o!r}\n")

print(f"  asdict(o):")
d = asdict(o)
print(f"    {d}")
print(f"    ⭐ REKURSIV — Manzil ham lug'at bo'ldi")
print(f"    d['teglar'] is o.teglar = {d['teglar'] is o.teglar}   "
      f"⭐ nusxa")

print(f"\n  astuple(o):")
print(f"    {astuple(o)}")

from dataclasses import replace

print(f"\n  replace(o, ism='Malika'):")
o2 = replace(o, ism="Malika")
print(f"    {o2!r}")
print(f"    o o'zgarmadi: {o.ism!r}")
print(f"    ⚠️ manzil BIR obyekt: {o2.manzil is o.manzil}")

print(f"\n  fields(o):")
for f in fields(o):
    print(f"    {f.name:<10} {str(f.type):<18} "
          f"factory={f.default_factory is not None}")

from dataclasses import is_dataclass

print(f"\n  is_dataclass():")
for x in [o, Odam, Manzil("a", "b"), "matn", 42, dict]:
    nom = x.__name__ if isinstance(x, type) else type(x).__name__
    print(f"    {nom:<12} {is_dataclass(x)}")

print(f"""
  ⚠️ asdict() CHUQUR nusxa — katta tuzilmalarda sekin.
     Faqat sathiy kerak bo'lsa:

       {{f.name: getattr(o, f.name) for f in fields(o)}}
""")

Natijaning muhim qismi:

text
=== 1. Yaratilgan metodlar ===

  @dataclass yaratgan metodlar:
    __init__     ✅    <function Nuqta.__init__ at 0x...>
    __repr__     ✅
    __eq__       ✅
    __hash__     ✅    None
    __lt__       ❌

  __init__ imzosi:
    (self, x: float, y: float = 0.0) -> None

=== ⚠️ 2. Tur ko'rsatkichi MAJBURIY ===

    a.__dict__  = {'maydon': 1, 'sukutli': 'sukut', '_ichki': True}
    ⭐ oddiy va KONSTANTA nusxa lug'atida YO'Q

=== 3. Parametrlar jadvali ===

  Sinf           repr     ==     <      hash     o'zgarmas
  ────────────────────────────────────────────────────────
  Sukut          ✅       ✅     ❌     ❌       ❌
  Toliq          ✅       ✅     ✅     ✅       ✅
  UnsafeHash     ✅       ✅     ❌     ✅       ❌

=== ⭐ 5. O'zgaruvchan sukut tuzog'i ===

    list         = list()  → ✅ ValueError: mutable default <class 'list'> for field x is not al

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

Misol 2 — __post_init__, InitVar, frozen

python
"""Tekshiruv, hisoblanadigan maydonlar va o'zgarmaslik."""

from __future__ import annotations

import hashlib
import math
from dataclasses import (dataclass, field, InitVar, replace,
                         FrozenInstanceError, asdict)
from datetime import date, datetime

print("=== 1. __post_init__ — tekshiruv ===\n")


@dataclass
class Togriturtburchak:
    en: float
    boy: float
    yuza: float = field(init=False)             # ⭐ hisoblanadi
    perimetr: float = field(init=False)
    kvadratmi: bool = field(init=False, repr=False)

    def __post_init__(self):
        """⭐ __init__ dan KEYIN chaqiriladi."""
        if self.en <= 0 or self.boy <= 0:
            raise ValueError(
                f"O'lchamlar musbat bo'lsin: {self.en}×{self.boy}"
            )
        self.yuza = self.en * self.boy
        self.perimetr = 2 * (self.en + self.boy)
        self.kvadratmi = math.isclose(self.en, self.boy)


t = Togriturtburchak(3, 4)
print(f"  Togriturtburchak(3, 4)")
print(f"    {t!r}")
print(f"    kvadratmi = {t.kvadratmi}")

print(f"\n  Togriturtburchak(5, 5)")
print(f"    kvadratmi = {Togriturtburchak(5, 5).kvadratmi}")

print(f"\n  Tekshiruv:")
for en, boy in [(0, 5), (-1, 5), (3, 0)]:
    try:
        Togriturtburchak(en, boy)
        n = "⚠️ o'tdi"
    except ValueError as e:
        n = f"✅ ValueError: {e}"
    print(f"    ({en}, {boy}) → {n}")

print(f"\n  ⚠️ Hisoblangan maydonlar ESKIRADI:")
t.en = 10
print(f"    t.en = 10 → yuza = {t.yuza}   ⚠️ 12 qoldi (40 bo'lishi kerak)")

print(f"""
  ✅ YECHIMLAR:
     1. frozen=True         — o'zgartirib bo'lmaydi
     2. @property           — har safar hisoblanadi
     3. __setattr__         — o'zgarganda qayta hisoblash
""")


@dataclass
class TogriturtburchakYaxshi:
    en: float
    boy: float

    def __post_init__(self):
        if self.en <= 0 or self.boy <= 0:
            raise ValueError("Musbat bo'lsin")

    @property
    def yuza(self) -> float:
        return self.en * self.boy               # ⭐ doim to'g'ri

    @property
    def perimetr(self) -> float:
        return 2 * (self.en + self.boy)


ty = TogriturtburchakYaxshi(3, 4)
print(f"  TogriturtburchakYaxshi(3, 4): yuza = {ty.yuza}")
ty.en = 10
print(f"    ty.en = 10 → yuza = {ty.yuza}   ✅ to'g'ri")


print("\n\n=== ⭐ 2. InitVar ===\n")


def xesh(parol: str, tuz: str = "wisar") -> str:
    return hashlib.sha256(f"{tuz}{parol}".encode()).hexdigest()[:16]


@dataclass
class Foydalanuvchi:
    ism: str
    email: str
    parol: InitVar[str]                         # ⭐ MAYDON EMAS
    tuz: InitVar[str] = "wisar"                 # ⭐ sukut bilan
    parol_xesh: str = field(init=False, default="", repr=False)
    yaratilgan: datetime = field(default_factory=datetime.now,
                                 repr=False)

    def __post_init__(self, parol: str, tuz: str):
        """⭐ InitVar lar ARGUMENT sifatida keladi."""
        if len(parol) < 8:
            raise ValueError("Parol kamida 8 belgi")
        self.parol_xesh = xesh(parol, tuz)
        self.email = self.email.lower().strip()

    def tekshir(self, parol: str, tuz: str = "wisar") -> bool:
        return self.parol_xesh == xesh(parol, tuz)


f = Foydalanuvchi("Aziz", "  AZIZ@Wisar.UZ  ", "maxfiy123")

print(f"  {f!r}\n")
print(f"    f.email      = {f.email!r}   ⭐ normallashtirildi")
print(f"    f.parol_xesh = {f.parol_xesh!r}")

print(f"\n  ⭐ parol OBYEKTDA SAQLANMAYDI:")
print(f"    hasattr(f, 'parol') = {hasattr(f, 'parol')}")
print(f"    f.__dict__ kalitlari: {sorted(f.__dict__)}")

print(f"\n  Parolni tekshirish:")
for p in ["maxfiy123", "notogri"]:
    print(f"    f.tekshir({p!r}) = {f.tekshir(p)}")

print(f"\n  Tekshiruv:")
try:
    Foydalanuvchi("A", "a@b.uz", "qisqa")
except ValueError as e:
    print(f"    Qisqa parol → ✅ ValueError: {e}")

print(f"\n  fields() da InitVar YO'Q:")
from dataclasses import fields as f_fields
print(f"    {[x.name for x in f_fields(Foydalanuvchi)]}")

print(f"""
  ⭐ InitVar ISHLATILISHI:
     • Parol → xesh
     • Konfiguratsiya → hisoblangan maydonlar
     • Fabrika argumenti → obyektda kerak emas
     • Tekshiruv rejimi (qat'iy/yumshoq)
""")


print("=== 3. frozen — o'zgarmas ===\n")


@dataclass(frozen=True, slots=True)
class Pul:
    miqdor: float
    valyuta: str = "UZS"

    def __post_init__(self):
        """⚠️ frozen da object.__setattr__ kerak."""
        if self.miqdor < 0:
            raise ValueError("Manfiy bo'lmasin")
        object.__setattr__(self, "valyuta", self.valyuta.upper())

    def __add__(self, b):
        if not isinstance(b, Pul):
            return NotImplemented
        if self.valyuta != b.valyuta:
            raise ValueError("Valyutalar mos emas")
        return Pul(self.miqdor + b.miqdor, self.valyuta)

    def __mul__(self, n):
        if not isinstance(n, (int, float)):
            return NotImplemented
        return Pul(self.miqdor * n, self.valyuta)

    __rmul__ = __mul__

    def __str__(self):
        return f"{self.miqdor:,.2f} {self.valyuta}"


p = Pul(1000.50, "uzs")
print(f"  Pul(1000.50, 'uzs') = {p!r}")
print(f"    valyuta = {p.valyuta!r}   ⭐ __post_init__ da katta harf")
print(f"    str: {p}")

print(f"\n  Himoya:")
for kod, f2 in [
    ("p.miqdor = 999",  lambda: setattr(p, "miqdor", 999)),
    ("p.valyuta = 'X'", lambda: setattr(p, "valyuta", "X")),
    ("del p.miqdor",    lambda: delattr(p, "miqdor")),
    ("p.yangi = 1",     lambda: setattr(p, "yangi", 1)),
]:
    try:
        f2(); n = "⚠️ O'TDI"
    except (FrozenInstanceError, AttributeError) as e:
        n = f"✅ {type(e).__name__}"
    print(f"    {kod:<20} {n}")

print(f"\n  ⭐ Hashable:")
print(f"    hash(p) bor: {hash(p) is not None}")
print(f"    {{Pul(1), Pul(1), Pul(2)}} → {len({Pul(1), Pul(1), Pul(2)})} element")

print(f"\n  Amallar (yangi obyekt):")
print(f"    p + Pul(500)   = {p + Pul(500)}")
print(f"    p * 3          = {p * 3}")
print(f"    3 * p          = {3 * p}")
print(f"    p o'zgarmadi   = {p}")

print(f"\n  replace():")
p2 = replace(p, miqdor=2000)
print(f"    replace(p, miqdor=2000) = {p2!r}")
print(f"    p = {p!r}   ✅ o'zgarmadi")


print("\n\n=== ⚠️ 4. frozen va __post_init__ ===\n")


@dataclass(frozen=True)
class Yomon:
    x: int
    ikki_x: int = field(init=False, default=0)

    def __post_init__(self):
        self.ikki_x = self.x * 2                # ❌ frozen!


@dataclass(frozen=True)
class Yaxshi:
    x: int
    ikki_x: int = field(init=False, default=0)

    def __post_init__(self):
        object.__setattr__(self, "ikki_x", self.x * 2)   # ✅


for Sinf in (Yomon, Yaxshi):
    try:
        o = Sinf(5)
        n = f"✅ ikki_x = {o.ikki_x}"
    except FrozenInstanceError as e:
        n = f"❌ FrozenInstanceError: {e}"
    print(f"  {Sinf.__name__:<10} {n}")

print(f"""
  ⭐ frozen da __setattr__ TAQIQLANGAN.
     Ichkarida object.__setattr__(self, nom, q) ishlating.

  ⚠️ Yoki umuman @property qiling:

     @property
     def ikki_x(self): return self.x * 2      ✅ soddaroq
""")


print("=== 5. slots ===\n")

import sys


@dataclass
class Oddiy:
    x: int
    y: int
    z: int


@dataclass(slots=True)
class Slotli:
    x: int
    y: int
    z: int


def olcham(Sinf, n: int = 20_000) -> int:
    """Bitta nusxaning o'rtacha hajmi — tracemalloc bilan.

    ⚠️ sys.getsizeof(o.__dict__) Python 3.11+ da lug'atni o'zi yaratib,
       natijani oshirib yuboradi (8.19-dars).
    """
    import tracemalloc
    joy = [None] * n
    tracemalloc.start()
    asos = tracemalloc.get_traced_memory()[0]
    for i in range(n):
        joy[i] = Sinf(1, 2, 3)
    jami = tracemalloc.get_traced_memory()[0] - asos
    tracemalloc.stop()
    return round(jami / n)


o1, o2 = Oddiy(1, 2, 3), Slotli(1, 2, 3)

print(f"  {'Sinf':<10} {'Bayt':>8} {'100k nusxa':>14} {'__dict__'}")
print("  " + "─" * 48)
for nom, o in [("Oddiy", o1), ("Slotli", o2)]:
    n = olcham(type(o))
    d = "✅" if hasattr(o, "__dict__") else "❌"
    print(f"  {nom:<10} {n:>8} {n * 100_000 / 1_048_576:>12.1f} MB  {d}")

print(f"\n  Yangi atribut:")
for nom, o in [("Oddiy", o1), ("Slotli", o2)]:
    try:
        o.yangi = 1
        n = "⚠️ o'tdi"
    except AttributeError:
        n = "✅ AttributeError"
    print(f"    {nom}: o.yangi = 1 → {n}")

print(f"""
  ⚠️ slots=True CHEKLOVLARI:
     • cached_property ishlamaydi (__dict__ yo'q)
     • weakref kerak bo'lsa weakref_slot=True (3.11+)
     • Ko'p merosda konflikt bo'lishi mumkin
     • Dinamik atribut qo'shib bo'lmaydi
""")


print("=== 6. kw_only (3.10+) ===\n")


@dataclass(kw_only=True)
class Sozlama:
    """⭐ Barcha maydonlar NOMLI."""
    host: str
    port: int
    debug: bool = False


try:
    Sozlama("localhost", 8000)
except TypeError as e:
    print(f"  Sozlama('localhost', 8000) → ❌ TypeError")
    print(f"    {str(e)[:66]}")

s = Sozlama(host="localhost", port=8000)
print(f"\n  Sozlama(host='localhost', port=8000) → ✅ {s}")


@dataclass
class Aralash:
    """⭐ Bir qismi pozitsion, bir qismi nomli."""
    a: int                                      # pozitsion
    b: int = 0                                  # pozitsion, sukutli
    c: int = field(kw_only=True)                # ⭐ NOMLI, sukutsiz!
    d: int = field(kw_only=True, default=10)


import inspect

print(f"\n  Aralash: a, b=0 pozitsion; c, d=10 nomli")
print(f"    imzo: {inspect.signature(Aralash.__init__)}")

x = Aralash(1, 2, c=3)
print(f"\n    Aralash(1, 2, c=3) = {x}")

try:
    Aralash(1, 2, 3)
except TypeError as e:
    print(f"    Aralash(1, 2, 3)   → ❌ TypeError")

print(f"""
  ⭐ kw_only AFZALLIKLARI:
     1. Sukutsiz maydon sukutlidan KEYIN turishi mumkin
     2. Chaqiruv o'qiladi: Sozlama(host=..., port=...)
     3. Maydon qo'shish/tartibini o'zgartirish xavfsiz
     4. Merosdagi tartib muammosi yo'q
""")

Natijaning muhim qismi:

text
=== 1. __post_init__ — tekshiruv ===

  Togriturtburchak(3, 4)
    Togriturtburchak(en=3, boy=4, yuza=12, perimetr=14)

  ⚠️ Hisoblangan maydonlar ESKIRADI:
    t.en = 10 → yuza = 12   ⚠️ 12 qoldi (40 bo'lishi kerak)

  TogriturtburchakYaxshi(3, 4): yuza = 12
    ty.en = 10 → yuza = 40   ✅ to'g'ri

=== ⭐ 2. InitVar ===

  ⭐ parol OBYEKTDA SAQLANMAYDI:
    hasattr(f, 'parol') = False
    f.__dict__ kalitlari: ['email', 'ism', 'parol_xesh', 'yaratilgan']

=== ⚠️ 4. frozen va __post_init__ ===

  Yomon      ❌ FrozenInstanceError: cannot assign to field 'ikki_x'
  Yaxshi     ✅ ikki_x = 10

=== 5. slots ===

  Sinf           Bayt     100k nusxa  __dict__
  ────────────────────────────────────────────────
  Oddiy            96          9.2 MB  ✅
  Slotli           56           5.3 MB  ❌

Nima ko'rsatdi: 2.2, 2.5-bo'limlar.

Misol 3 — Meros va tuzoqlar

python
"""dataclass merosidagi nozikliklar."""

from dataclasses import dataclass, field, fields, replace
from typing import ClassVar
import inspect

print("=== ⚠️ 1. Sukut qiymatlar tartibi ===\n")


@dataclass
class Ota:
    a: int
    b: int = 0


print(f"  @dataclass class Ota: a: int; b: int = 0")
print(f"    __init__: {inspect.signature(Ota.__init__)}\n")

print(f"  Avlodda SUKUTSIZ maydon:")
try:
    @dataclass
    class BolaYomon(Ota):
        c: int                                  # ❌
    print(f"    ⚠️ o'tdi")
except TypeError as e:
    print(f"    ❌ TypeError: {e}")

print(f"""
  ⭐ SABAB: yaratiladigan __init__ bo'lardi:
       def __init__(self, a, b=0, c):     ❌ sintaksis xatosi
""")

print(f"  ✅ YECHIM 1 — sukut qiymat:")


@dataclass
class Bola1(Ota):
    c: int = 0


print(f"    {inspect.signature(Bola1.__init__)}")

print(f"\n  ✅ YECHIM 2 — kw_only (3.10+):")


@dataclass(kw_only=True)
class OtaKw:
    a: int
    b: int = 0


@dataclass(kw_only=True)
class BolaKw(OtaKw):
    c: int                                      # ✅ ishlaydi


print(f"    {inspect.signature(BolaKw.__init__)}")
print(f"    BolaKw(a=1, c=3) = {BolaKw(a=1, c=3)}")

print(f"\n  ✅ YECHIM 3 — faqat shu maydon kw_only:")


@dataclass
class Bola3(Ota):
    c: int = field(kw_only=True)


print(f"    {inspect.signature(Bola3.__init__)}")
print(f"    Bola3(1, 2, c=3) = {Bola3(1, 2, c=3)}")


print("\n\n=== 2. Maydon tartibi merosda ===\n")


@dataclass
class Asos:
    a: int
    b: str = "asos-b"
    c: float = 0.0


@dataclass
class Avlod(Asos):
    b: str = "avlod-b"                          # ⭐ qayta aniqlash
    d: bool = True                              # yangi


print(f"  Asos:  a, b='asos-b', c=0.0")
print(f"  Avlod: b='avlod-b' (qayta), d=True (yangi)\n")

print(f"  Asos.__init__:  {inspect.signature(Asos.__init__)}")
print(f"  Avlod.__init__: {inspect.signature(Avlod.__init__)}")

print(f"\n  ⭐ b HALI HAM ikkinchi o'rinda — tartib SAQLANADI")
print(f"     (faqat sukut qiymati o'zgardi)")

print(f"\n  fields(Avlod):")
for f in fields(Avlod):
    print(f"    {f.name:<6} {str(f.type):<10} sukut={f.default!r}")

print(f"\n  Avlod(1) = {Avlod(1)}")


print("\n\n=== ⚠️ 3. eq va hash merosda ===\n")


@dataclass
class OtaEq:
    x: int


@dataclass
class BolaEq(OtaEq):
    y: int = 0


a, b = OtaEq(1), BolaEq(1, 0)

print(f"  OtaEq(1) == BolaEq(1, 0) → {a == b}")
print(f"  ⭐ SABAB: __eq__ da other.__class__ is self.__class__")
print(f"     Turli sinflar → NotImplemented → False")

print(f"\n  Yaratilgan __eq__ mantiqi:")
print(f"""
     def __eq__(self, other):
         if other.__class__ is self.__class__:      ⭐ ANIQ tur
             return (self.x,) == (other.x,)
         return NotImplemented
""")


@dataclass(frozen=True)
class OtaFrozen:
    x: int


try:
    @dataclass
    class BolaOzgaruvchan(OtaFrozen):
        y: int = 0
except TypeError as e:
    print(f"  frozen Ota → o'zgaruvchan Bola:")
    print(f"    ❌ TypeError: {e}")

try:
    @dataclass(frozen=True)
    class BolaFrozen(OtaFrozen):
        y: int = 0
    print(f"\n  frozen Ota → frozen Bola: ✅ ishlaydi")
    print(f"    BolaFrozen(1, 2) = {BolaFrozen(1, 2)}")
except TypeError as e:
    print(f"    ❌ {e}")

print(f"""
  ⭐ QOIDA: frozen ierarxiya bo'ylab BIR XIL bo'lishi kerak.
     frozen → frozen ✅
     oddiy  → oddiy  ✅
     frozen → oddiy  ❌
     oddiy  → frozen ❌
""")


print("=== ⚠️ 4. ClassVar va meros ===\n")


@dataclass
class Hisoblagich:
    JAMI: ClassVar[int] = 0                     # ⭐ maydon EMAS
    _KESH: ClassVar[dict] = {}                  # ⭐ o'zgaruvchan, lekin OK

    nom: str

    def __post_init__(self):
        Hisoblagich.JAMI += 1
        Hisoblagich._KESH[self.nom] = self


h1, h2, h3 = Hisoblagich("a"), Hisoblagich("b"), Hisoblagich("c")

print(f"  fields(): {[f.name for f in fields(Hisoblagich)]}")
print(f"    ⭐ JAMI va _KESH YO'Q (ClassVar)")
print(f"\n  Hisoblagich.JAMI = {Hisoblagich.JAMI}")
print(f"  Kesh: {sorted(Hisoblagich._KESH)}")
print(f"  h1.__dict__ = {h1.__dict__}")

print(f"\n  ⭐ ClassVar o'zgaruvchan bo'lishi MUMKIN:")
print(f"     _KESH: ClassVar[dict] = {{}}    ✅ ruxsat")
print(f"     kesh: dict = {{}}               ❌ ValueError")

try:
    @dataclass
    class Xato:
        kesh: dict = {}
except ValueError as e:
    print(f"\n     Tasdiqlash: ❌ ValueError: {str(e)[:50]}")


print("\n\n=== 5. Metodlar va meros ===\n")


@dataclass
class Shakl:
    nom: str

    def yuza(self) -> float:
        raise NotImplementedError

    def tavsif(self) -> str:
        """⭐ Meros olinadi."""
        return f"{self.nom}: yuza={self.yuza():.2f}"

    @classmethod
    def turlar(cls) -> list[str]:
        return [S.__name__ for S in cls.__subclasses__()]


@dataclass
class Doira(Shakl):
    radius: float = 1.0

    def yuza(self) -> float:
        return 3.14159 * self.radius ** 2


@dataclass
class Kvadrat(Shakl):
    tomon: float = 1.0

    def yuza(self) -> float:
        return self.tomon ** 2


@dataclass
class Togriturtburchak(Shakl):
    en: float = 1.0
    boy: float = 1.0

    def yuza(self) -> float:
        return self.en * self.boy


SHAKLLAR = [Doira("Doira", 5), Kvadrat("Kvadrat", 4),
            Togriturtburchak("To'rtburchak", 3, 6)]

print(f"  Shakl turlari: {Shakl.turlar()}\n")
for s in SHAKLLAR:
    print(f"    {s.tavsif()}")
    print(f"      repr: {s!r}")

print(f"\n  Jami yuza: {sum(s.yuza() for s in SHAKLLAR):.2f}")

print(f"""
  ⭐ dataclass METODLARNI to'sMAYDI:
     • Oddiy metodlar ✅
     • @property ✅
     • @classmethod, @staticmethod ✅
     • Dunder metodlar (agar dataclass yaratmasa) ✅
""")


print("=== ⚠️ 6. property bilan tuzoq ===\n")

print("""  ⚠️ MUAMMO: dataclass va property nomlari to'qnashadi

     @dataclass
     class A:
         x: int

         @property
         def x(self): return self._x       # ⚠️ maydon to'sildi

  ⭐ SABAB: @property sinf atributi bo'lib qoladi,
     dataclass uni MAYDON SUKUTI deb hisoblaydi.
""")


@dataclass
class Muammoli:
    x: int = 0

    @property
    def x(self):                                # ⚠️
        return self._x

    @x.setter
    def x(self, q):
        self._x = q


try:
    m = Muammoli(5)
    print(f"  Muammoli(5).x = {m.x}")
    m2 = Muammoli()
    print(f"  Muammoli().x = {m2.x!r}   ⚠️ property obyekti!")
except Exception as e:
    print(f"  ❌ {type(e).__name__}: {e}")

print(f"\n  ✅ YECHIM 1 — boshqa nom:")


@dataclass
class Yechim1:
    _x: int = 0

    @property
    def x(self) -> int:
        return self._x

    @x.setter
    def x(self, q: int) -> None:
        if q < 0:
            raise ValueError("Manfiy bo'lmasin")
        self._x = q


y1 = Yechim1(5)
print(f"    Yechim1(5).x = {y1.x}")
y1.x = 10
print(f"    y1.x = 10 → {y1.x}, y1._x = {y1._x}")
try:
    y1.x = -1
except ValueError as e:
    print(f"    y1.x = -1 → ✅ ValueError: {e}")

print(f"\n  ✅ YECHIM 2 — __post_init__ da tekshirish:")


@dataclass
class Yechim2:
    x: int = 0

    def __post_init__(self):
        if self.x < 0:
            raise ValueError("Manfiy bo'lmasin")


print(f"    Yechim2(5) = {Yechim2(5)}")
try:
    Yechim2(-1)
except ValueError as e:
    print(f"    Yechim2(-1) → ✅ ValueError: {e}")
print(f"    ⚠️ Lekin y.x = -1 keyin o'tadi")

print(f"\n  ✅ YECHIM 3 — deskriptor (8.15-dars):")


class Musbat:
    def __set_name__(self, ega, nom):
        self.nom = f"_{nom}"

    def __get__(self, obj, ega=None):
        if obj is None: return self
        return getattr(obj, self.nom, 0)

    def __set__(self, obj, q):
        if q < 0:
            raise ValueError(f"{self.nom[1:]} manfiy bo'lmasin")
        setattr(obj, self.nom, q)


@dataclass
class Yechim3:
    x: int = Musbat()                           # ⭐ deskriptor sukut


y3 = Yechim3(5)
print(f"    Yechim3(5).x = {y3.x}")
try:
    y3.x = -1
except ValueError as e:
    print(f"    y3.x = -1 → ✅ ValueError: {e}")

print(f"""
  ⭐ Deskriptor + dataclass:
     Python 3.11+ da deskriptor sukut sifatida ishlaydi.
     __set_name__ chaqiriladi, tekshiruv har yozishda.
""")

Natijaning muhim qismi:

text
=== ⚠️ 1. Sukut qiymatlar tartibi ===

  Avlodda SUKUTSIZ maydon:
    ❌ TypeError: non-default argument 'c' follows default argument

  ✅ YECHIM 2 — kw_only (3.10+):
    (self, *, a: int, b: int = 0, c: int) -> None

=== 2. Maydon tartibi merosda ===

  Asos.__init__:  (self, a: int, b: str = 'asos-b', c: float = 0.0)
  Avlod.__init__: (self, a: int, b: str = 'avlod-b', c: float = 0.0,
                   d: bool = True)

  ⭐ b HALI HAM ikkinchi o'rinda — tartib SAQLANADI

=== ⚠️ 3. eq va hash merosda ===

  OtaEq(1) == BolaEq(1, 0) → False

  frozen Ota → o'zgaruvchan Bola:
    ❌ TypeError: cannot inherit non-frozen dataclass from a frozen one

=== ⚠️ 6. property bilan tuzoq ===

  Muammoli().x = <property object at 0x...>   ⚠️ property obyekti!

Nima ko'rsatdi: 2.6-bo'lim.

Misol 4 — Amaliy: konfiguratsiya tizimi

python
"""dataclass ning barcha imkoniyatlari bir loyihada."""

from __future__ import annotations

import json
import os
import re
from dataclasses import (dataclass, field, fields, asdict, replace,
                         InitVar, MISSING)
from datetime import datetime, timedelta
from enum import Enum
from pathlib import Path
from typing import Any, ClassVar

print("=== KONFIGURATSIYA TIZIMI ===\n")


class Muhit(str, Enum):
    ISHLAB_CHIQISH = "ishlab_chiqish"
    SINOV = "sinov"
    ISHLAB_CHIQARISH = "ishlab_chiqarish"

    def __str__(self): return self.value


@dataclass(frozen=True, slots=True, order=True)
class Versiya:
    """⭐ frozen + slots + order — barchasi bepul."""
    katta: int
    kichik: int = 0
    tuzatish: int = 0

    @classmethod
    def parse(cls, s: str) -> "Versiya":
        m = re.match(r"^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?$", s.strip())
        if not m:
            raise ValueError(f"Noto'g'ri versiya: {s!r}")
        return cls(*(int(q) if q else 0 for q in m.groups()))

    def __str__(self):
        return f"{self.katta}.{self.kichik}.{self.tuzatish}"


@dataclass(frozen=True, slots=True)
class Manzil:
    """Ulanish manzili."""
    host: str = "localhost"
    port: int = 8000
    ssl: bool = False

    def __post_init__(self):
        if not 1 <= self.port <= 65535:
            raise ValueError(f"Port 1–65535: {self.port}")
        if not self.host.strip():
            raise ValueError("Host bo'sh bo'lmasin")
        object.__setattr__(self, "host", self.host.strip().lower())

    @property
    def url(self) -> str:
        sxema = "https" if self.ssl else "http"
        port = "" if self.port in (80, 443) else f":{self.port}"
        return f"{sxema}://{self.host}{port}"

    @classmethod
    def parse(cls, s: str) -> "Manzil":
        m = re.match(r"^(?:(https?)://)?([^:/]+)(?::(\d+))?", s.strip())
        if not m:
            raise ValueError(f"Noto'g'ri manzil: {s!r}")
        sxema, host, port = m.groups()
        ssl = sxema == "https"
        return cls(host, int(port) if port else (443 if ssl else 80), ssl)

    def __str__(self): return self.url


@dataclass(kw_only=True)
class BazaSozlama:
    """⭐ kw_only — barcha maydon nomli."""

    manzil: Manzil = field(default_factory=lambda: Manzil("localhost", 5432))
    baza: str = "wisar"
    foydalanuvchi: str = "postgres"
    parol: InitVar[str] = ""                    # ⭐ saqlanmaydi
    _parol_bor: bool = field(init=False, default=False, repr=False)
    hovuz_hajmi: int = 10
    vaqt_chegarasi: float = 30.0

    # ⭐ Ichki holat
    _ulanish_url: str = field(init=False, default="", repr=False)

    def __post_init__(self, parol: str):
        if self.hovuz_hajmi < 1:
            raise ValueError(f"hovuz_hajmi >= 1: {self.hovuz_hajmi}")
        self._parol_bor = bool(parol)
        yashirin = "***" if parol else ""
        self._ulanish_url = (
            f"postgresql://{self.foydalanuvchi}:{yashirin}"
            f"@{self.manzil.host}:{self.manzil.port}/{self.baza}"
        )

    @property
    def ulanish_url(self) -> str:
        return self._ulanish_url


@dataclass(kw_only=True)
class Sozlama:
    """Asosiy konfiguratsiya."""

    # ⭐ ClassVar — maydon emas
    VERSIYA: ClassVar[Versiya] = Versiya(2, 1, 0)
    _NUSXALAR: ClassVar[int] = 0

    # Majburiy
    nom: str

    # Sukutli
    muhit: Muhit = Muhit.ISHLAB_CHIQISH
    manzil: Manzil = field(default_factory=Manzil)
    baza: BazaSozlama = field(default_factory=BazaSozlama)

    # O'zgaruvchan — default_factory
    ruxsat_etilgan: list[str] = field(default_factory=lambda: ["*"])
    xususiyatlar: dict[str, bool] = field(default_factory=dict)
    teglar: set[str] = field(default_factory=set)

    # Sirlar — repr da yo'q
    api_kalit: str = field(default="", repr=False)

    # Taqqoslashda yo'q
    yaratilgan: datetime = field(default_factory=datetime.now,
                                 compare=False, repr=False)

    # Hisoblanadi
    debug: bool = field(init=False, default=False)
    _id: int = field(init=False, default=0, compare=False, repr=False)

    def __post_init__(self):
        if not self.nom.strip():
            raise ValueError("Nom bo'sh bo'lmasin")
        self.nom = self.nom.strip()

        if isinstance(self.muhit, str):
            self.muhit = Muhit(self.muhit)

        # ⭐ Bog'liq qiymat
        self.debug = self.muhit != Muhit.ISHLAB_CHIQARISH

        if self.muhit == Muhit.ISHLAB_CHIQARISH:
            if "*" in self.ruxsat_etilgan:
                raise ValueError(
                    "Ishlab chiqarishda '*' ruxsat etilmaydi"
                )
            if not self.api_kalit:
                raise ValueError("Ishlab chiqarishda api_kalit majburiy")

        Sozlama._NUSXALAR += 1
        self._id = Sozlama._NUSXALAR

    # ═══ FABRIKA METODLARI ═══

    @classmethod
    def ishlab_chiqish(cls, nom: str = "dev", **kw) -> "Sozlama":
        return cls(nom=nom, muhit=Muhit.ISHLAB_CHIQISH,
                   manzil=Manzil("localhost", 8000), **kw)

    @classmethod
    def sinov(cls, nom: str = "test", **kw) -> "Sozlama":
        return cls(nom=nom, muhit=Muhit.SINOV,
                   manzil=Manzil("0.0.0.0", 8001),
                   ruxsat_etilgan=["localhost"], **kw)

    @classmethod
    def ishlab_chiqarish(cls, nom: str, api_kalit: str,
                         **kw) -> "Sozlama":
        kw.setdefault("ruxsat_etilgan", ["wisar.uz", "api.wisar.uz"])
        return cls(nom=nom, muhit=Muhit.ISHLAB_CHIQARISH,
                   manzil=Manzil("0.0.0.0", 443, ssl=True),
                   api_kalit=api_kalit, **kw)

    @classmethod
    def jsondan(cls, matn: str) -> "Sozlama":
        m = json.loads(matn)
        if "manzil" in m and isinstance(m["manzil"], str):
            m["manzil"] = Manzil.parse(m["manzil"])
        elif "manzil" in m and isinstance(m["manzil"], dict):
            m["manzil"] = Manzil(**m["manzil"])
        if "teglar" in m:
            m["teglar"] = set(m["teglar"])
        nomlar = {f.name for f in fields(cls) if f.init}
        notogri = set(m) - nomlar
        if notogri:
            raise ValueError(f"Noma'lum maydonlar: {sorted(notogri)}")
        return cls(**m)

    @classmethod
    def muhitdan(cls, muhit_ozg: dict[str, str],
                 prefiks: str = "APP_") -> "Sozlama":
        m: dict[str, Any] = {}
        for f in fields(cls):
            if not f.init:
                continue
            kalit = f"{prefiks}{f.name.upper()}"
            if kalit not in muhit_ozg:
                continue
            xom = muhit_ozg[kalit]
            tur = f.type
            if tur == "bool" or tur is bool:
                m[f.name] = xom.lower() in ("1", "true", "ha")
            elif tur == "int" or tur is int:
                m[f.name] = int(xom)
            elif "list" in str(tur):
                m[f.name] = [q.strip() for q in xom.split(",")]
            elif "set" in str(tur):
                m[f.name] = {q.strip() for q in xom.split(",")}
            else:
                m[f.name] = xom
        return cls(**m)

    # ═══ AMALLAR ═══

    def bilan(self, **ozgarishlar) -> "Sozlama":
        """⭐ replace() ning qulay o'ramchisi."""
        return replace(self, **ozgarishlar)

    def lugat(self, sirlar_bilan: bool = False) -> dict[str, Any]:
        d = asdict(self)
        d["muhit"] = str(self.muhit)
        d["teglar"] = sorted(self.teglar)
        d.pop("_id", None)
        d.pop("yaratilgan", None)
        if not sirlar_bilan:
            d["api_kalit"] = "***" if self.api_kalit else ""
            d["baza"].pop("_ulanish_url", None)
        return d

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

    @property
    def url(self) -> str:
        return self.manzil.url


print("1. Fabrika metodlari:\n")

s_dev = Sozlama.ishlab_chiqish()
s_test = Sozlama.sinov(teglar={"ci", "avtomatik"})
s_prod = Sozlama.ishlab_chiqarish("wisar-prod", api_kalit="sk-abc123")

for s in (s_dev, s_test, s_prod):
    print(f"  {str(s.muhit):<18} {s.url:<28} debug={s.debug}")
    print(f"  {'':<18} ruxsat: {s.ruxsat_etilgan}")

print(f"\n  Ishlab chiqarish tekshiruvi:")
for tavsif, kw in [
    ("api_kalit yo'q",  dict(nom="p", muhit=Muhit.ISHLAB_CHIQARISH,
                             ruxsat_etilgan=["wisar.uz"])),
    ("'*' ruxsat",      dict(nom="p", muhit=Muhit.ISHLAB_CHIQARISH,
                             api_kalit="k", ruxsat_etilgan=["*"])),
    ("nom bo'sh",       dict(nom="   ")),
]:
    try:
        Sozlama(**kw)
        n = "⚠️ o'tdi"
    except ValueError as e:
        n = f"✅ ValueError: {e}"
    print(f"    {tavsif:<18} {n}")


print("\n\n2. ⭐ InitVar — parol saqlanmaydi:\n")

b = BazaSozlama(baza="wisar_prod", foydalanuvchi="app",
                parol="juda-maxfiy-parol", hovuz_hajmi=20)

print(f"  {b!r}\n")
print(f"    parol nusxada:    {'parol' in vars(b)}   ⭐ saqlanmadi")
print(f"    hasattr(b, 'parol'): {hasattr(b, 'parol')}   ⚠️ chalg'ituvchi!")
print(f"      Sabab: InitVar standart qiymati (= \"\") SINF atributi bo'lib")
print(f"      qoladi: BazaSozlama.parol = {BazaSozlama.parol!r}. Nusxada esa yo'q.")
print(f"    _parol_bor:       {b._parol_bor}")
print(f"    ulanish_url:      {b.ulanish_url}")
print(f"    ⭐ Parol XESHLANMAGAN ham, SAQLANMAGAN ham")


print("\n\n3. O'zgaruvchan sukutlar ajralgan:\n")

a1, a2 = Sozlama(nom="a"), Sozlama(nom="b")
a1.ruxsat_etilgan.append("qo'shildi")
a1.xususiyatlar["yangi"] = True
a1.teglar.add("teg")

print(f"  a1.ruxsat_etilgan = {a1.ruxsat_etilgan}")
print(f"  a2.ruxsat_etilgan = {a2.ruxsat_etilgan}   ✅ ajralgan")
print(f"  a1.xususiyatlar   = {a1.xususiyatlar}")
print(f"  a2.xususiyatlar   = {a2.xususiyatlar}   ✅")
print(f"\n  a1.baza is a2.baza = {a1.baza is a2.baza}   ✅ ajralgan")


print("\n\n4. replace / bilan:\n")

asl = Sozlama.ishlab_chiqish("asl")
yangi = asl.bilan(nom="yangi", manzil=Manzil("api.wisar.uz", 443,
                                             ssl=True))

print(f"  asl:   nom={asl.nom!r}, url={asl.url}")
print(f"  yangi: nom={yangi.nom!r}, url={yangi.url}")
print(f"  asl o'zgarmadi: {asl.nom!r} ✅")

print(f"\n  ⚠️ replace SATHIY nusxa:")
print(f"    yangi.teglar is asl.teglar = {yangi.teglar is asl.teglar}")
print(f"    ⭐ default_factory qayta ishlamaydi — mavjud qiymat ko'chadi")

print(f"\n  init=False maydonlar:")
try:
    replace(asl, debug=True)
except (ValueError, TypeError) as e:
    print(f"    replace(asl, debug=True) → ✅ {type(e).__name__}")


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

print(f"  s_prod.lugat() (sirlarsiz):")
d = s_prod.lugat()
for k, v in list(d.items())[:8]:
    print(f"    {k:<18} {v!r}")

print(f"\n  ⭐ api_kalit: {d['api_kalit']!r} (yashirilgan)")

print(f"\n  JSON:")
for q in s_dev.json(indent=2).split("\n")[:12]:
    print(f"    {q}")

print(f"\n  jsondan() bilan qayta yaratish:")
JSON = json.dumps({
    "nom": "json-dan",
    "muhit": "sinov",
    "manzil": {"host": "api.local", "port": 9000},
    "teglar": ["a", "b"],
})
s_json = Sozlama.jsondan(JSON)
print(f"    {s_json.nom!r}, {s_json.url}, teglar={sorted(s_json.teglar)}")

try:
    Sozlama.jsondan('{"nom": "x", "xato_maydon": 1}')
except ValueError as e:
    print(f"    Noma'lum maydon → ✅ ValueError: {e}")


print("\n\n6. Muhit o'zgaruvchilaridan:\n")

MUHIT_OZG = {
    "APP_NOM": "muhitdan",
    "APP_MUHIT": "sinov",
    "APP_RUXSAT_ETILGAN": "localhost, 127.0.0.1, api.local",
    "APP_TEGLAR": "ci,test",
    "APP_API_KALIT": "sk-muhit",
}

s_env = Sozlama.muhitdan(MUHIT_OZG)
print(f"  {s_env.nom!r}, muhit={s_env.muhit}")
print(f"    ruxsat_etilgan = {s_env.ruxsat_etilgan}")
print(f"    teglar         = {sorted(s_env.teglar)}")


print("\n\n7. Versiya (frozen + order + slots):\n")

VERSIYALAR = [Versiya.parse(s) for s in
              ["2.1.0", "1.0", "2.0.5", "v3", "2.1.1"]]

print(f"  Saralangan: {[str(v) for v in sorted(VERSIYALAR)]}")
print(f"  Eng yangi:  {max(VERSIYALAR)}")
print(f"  Joriy:      {Sozlama.VERSIYA}")
print(f"\n  Hashable: {{v1, v1, v2}} → "
      f"{len({Versiya(1), Versiya(1), Versiya(2)})} element")

v = Versiya(2, 1, 0)
try:
    v.katta = 3
except Exception as e:
    print(f"  v.katta = 3 → ✅ {type(e).__name__}")

import sys
print(f"\n  Xotira (slots=True): {sys.getsizeof(v)} bayt")
print(f"    __dict__ bormi: {hasattr(v, '__dict__')}")


print("\n\n8. Maydonlar tahlili:\n")

print(f"  {'Maydon':<18} {'init':<6} {'repr':<6} {'compare':<9} "
      f"{'factory'}")
print("  " + "─" * 56)
for f in fields(Sozlama):
    fac = "✅" if f.default_factory is not MISSING else ""
    print(f"  {f.name:<18} {f.init!s:<6} {f.repr!s:<6} "
          f"{f.compare!s:<9} {fac}")

print(f"\n  ClassVar (maydon emas): VERSIYA, _NUSXALAR")
print(f"  Yaratilgan nusxalar: {Sozlama._NUSXALAR}")

print(f"""

  ⭐ BU LOYIHADA ISHLATILGAN:

     @dataclass(frozen=True, slots=True, order=True)   Versiya
     @dataclass(kw_only=True)                          Sozlama, BazaSozlama

     field(default_factory=...)    o'zgaruvchan sukutlar
     field(repr=False)             sirlar (api_kalit)
     field(compare=False)          vaqt tamg'asi, _id
     field(init=False)             hisoblanadigan (debug, _id)

     InitVar[str]                  parol — saqlanmaydi
     ClassVar[...]                 VERSIYA, _NUSXALAR

     __post_init__                 tekshiruv, normallashtirish,
                                   bog'liq qiymatlar
     object.__setattr__            frozen ichida yozish

     @classmethod fabrikalar       ishlab_chiqish, sinov,
                                   ishlab_chiqarish, jsondan, muhitdan
     @property                     url, ulanish_url
     replace()                     bilan() orqali

  ⭐ NATIJA: ~200 qator kod bilan to'liq konfiguratsiya
     tizimi — tekshiruv, serializatsiya, meros, o'zgarmaslik.
""")

Natijaning muhim qismi:

text
1. Fabrika metodlari:

  ishlab_chiqish     http://localhost:8000        debug=True
                     ruxsat: ['*']
  ishlab_chiqarish   https://0.0.0.0              debug=False
                     ruxsat: ['wisar.uz', 'api.wisar.uz']

  Ishlab chiqarish tekshiruvi:
    api_kalit yo'q     ✅ ValueError: Ishlab chiqarishda api_kalit majburiy
    '*' ruxsat         ✅ ValueError: Ishlab chiqarishda '*' ruxsat etilmaydi

2. ⭐ InitVar — parol saqlanmaydi:

    parol nusxada:    False   ⭐ saqlanmadi
    hasattr(b, 'parol'): True   ⚠️ chalg'ituvchi!
    ulanish_url:      postgresql://app:***@localhost:5432/wisar_prod

4. replace / bilan:

  ⚠️ replace SATHIY nusxa:
    yangi.teglar is asl.teglar = True

7. Versiya (frozen + order + slots):

  Saralangan: ['1.0.0', '2.0.5', '2.1.0', '2.1.1', '3.0.0']
  Xotira (slots=True): 56 bayt

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


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

Noto'g'ri fikr To'g'risi
"x = 5 ham maydon" Tur ko'rsatkichi kerak: x: int = 5
"dataclass turni tekshiradi" Faqat hujjat; pydantic tekshiradi
"@dataclass __hash__ beradi" frozen=True kerak
"asdict sathiy nusxa" Chuqur rekursiv nusxa
"replace chuqur nusxa qiladi" Sathiy — konteynerlar bir obyekt
"frozen to'liq o'zgarmas" Ichki list o'zgaradi
"Meros bilan tartib erkin" Sukutsiz maydon sukutlidan keyin bo'lmaydi
"ClassVar maydon" Ataylab maydon emas

6. Keng tarqalgan xatolar va yechimlari

1. Tur ko'rsatkichi yo'q

python
@dataclass
class A:
    x = 5                       # ⚠️ maydon emas
    x: int = 5                  # ✅

2. O'zgaruvchan sukut

python
teglar: list = []                           # ❌ ValueError
teglar: list = field(default_factory=list)  # ✅

3. frozen da __post_init__

python
def __post_init__(self):
    self.y = self.x * 2                     # ❌ FrozenInstanceError
    object.__setattr__(self, "y", self.x*2) # ✅

4. Merosda sukutsiz maydon

python
@dataclass
class B(A):
    c: int                      # ❌ TypeError
    c: int = 0                  # ✅
    # yoki @dataclass(kw_only=True)

5. property nomi maydon bilan bir xil

python
@dataclass
class A:
    x: int = 0
    @property
    def x(self): ...            # ⚠️ to'qnashuv
    # ✅ _x: int = 0 + property x

6. frozen ichida list

python
@dataclass(frozen=True)
class A:
    lst: list = field(default_factory=list)
a.lst.append(1)                 # ⚠️ o'tadi!
    lst: tuple = ()             # ✅

7. Hisoblangan maydonni saqlash

python
yuza: float = field(init=False)     # ⚠️ eskiradi
@property
def yuza(self): ...                 # ✅

8. eq=True + hashable kerak

python
@dataclass
class A:
    x: int
{A(1)}                          # ❌ unhashable
@dataclass(frozen=True)         # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 8.3-dars (o'tilgan): __init__ va sukut tuzoqlari
  • 8.11, 8.12-darslar: __repr__, __eq__, __hash__
  • 8.15-dars: property va deskriptorlar
  • 8.19-dars: __slots__
  • 7.14-dars: tur ko'rsatkichlari
  • attrs, pydantic, msgspec — muqobillar
  • PEP 557: Data Classes

8. Eng yaxshi amaliyotlar

  1. O'zgaruvchan sukut — field(default_factory=...). Har doim.

  2. Ma'lumot obyekti bo'lsa frozen=True. Hashable va xavfsiz.

  3. Ko'p maydon bo'lsa kw_only=True. O'qiladigan va kengaytiriladigan.

  4. Sirlar — field(repr=False). Jurnalga tushmasin.

  5. Hisoblangan qiymat — @property. field(init=False) eskiradi.

  6. Tekshiruv — __post_init__ da. Yoki pydantic.

  7. InitVar — saqlanmasligi kerak bo'lgan argument uchun.

  8. Ko'p nusxa bo'lsa slots=True. 3× kam xotira.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  @dataclass
    class A: x: int = 1
    print(A() == A(), A.__hash__)
2.  @dataclass(frozen=True)
    class A: x: int = 1
    print(A.__hash__ is not None)
3.  @dataclass
    class A:
        x = 5
        y: int = 10
    print([f.name for f in fields(A)])
4.  @dataclass
    class A: x: int = 0
    try:
        @dataclass
        class B(A): y: int
    except TypeError: print("xato")
5.  @dataclass
    class A: lst: list = field(default_factory=list)
    a, b = A(), A()
    a.lst.append(1)
    print(b.lst)
6.  @dataclass
    class A: x: int
    print(asdict(A(1)))
7.  @dataclass
    class A: x: list
    a = A([1])
    print(asdict(a)["x"] is a.x)
8.  @dataclass(order=True)
    class A: x: int; y: int
    print(A(1, 2) < A(1, 3), A(2, 0) < A(1, 9))
9.  @dataclass
    class A:
        x: int
        JAMI: ClassVar[int] = 0
    print(len(fields(A)))
10. @dataclass
    class A: x: int = 0
    print(replace(A(1), x=2))
11. @dataclass(frozen=True)
    class A: lst: tuple = ()
    a = A((1, 2))
    print(hash(a) is not None)
12. @dataclass
    class A:
        x: int
        y: int = field(init=False, default=0)
        def __post_init__(self): self.y = self.x * 2
    print(A(5))
Javoblar
  1. True None
  2. True
  3. ['y'] — x tur ko'rsatkichisiz
  4. xato — A.x sukutli, B.y sukutsiz → __init__(self, x=0, y) yaroqsiz
  5. []
  6. {'x': 1}
  7. False — chuqur nusxa
  8. True False
  9. 1 — ClassVar maydon emas
  10. A(x=2)
  11. True
  12. A(x=5, y=10)

Vazifa 2: Xatolarni tuzating

python
1.  @dataclass
    class A: teglar: list = []
2.  @dataclass
    class A:
        x = 5
3.  @dataclass(frozen=True)
    class A:
        x: int
        y: int = field(init=False, default=0)
        def __post_init__(self): self.y = self.x * 2
4.  @dataclass
    class B(A): c: int          # A da sukutli maydon bor
5.  @dataclass
    class A:
        x: int
        yuza: float = field(init=False, default=0)
6.  @dataclass
    class A: parol: str
7.  @dataclass
    class A: x: int
    s = {A(1)}
8.  @dataclass(frozen=True)
    class A: lst: list = field(default_factory=list)
Javoblar
python
1.  teglar: list = field(default_factory=list)
2.  x: int = 5
3.  object.__setattr__(self, "y", self.x * 2)
4.  c: int = 0  yoki @dataclass(kw_only=True)
5.  @property def yuza(self): ...
6.  parol: str = field(repr=False)  (yoki InitVar)
7.  @dataclass(frozen=True)
8.  lst: tuple = ()  (frozen bilan o'zgarmas tur)

Vazifa 3: Buyurtma modeli

Yozing:

  1. Mahsulot — frozen, slots, order
  2. Element — mahsulot + soni, @property jami
  3. Buyurtma — kw_only, elementlar ro'yxati
  4. __post_init__ da tekshiruv
  5. InitVar bilan chegirma kodi (saqlanmasin)
  6. asdict, replace, fabrika metodlari
  7. Sirlar repr da yo'q

Vazifa 4: dataclass tekshiruvchisi

Vosita yozing:

  1. O'zgaruvchan sukutlarni topsin (field siz)
  2. Tur ko'rsatkichisiz "maydonlar"ni
  3. frozen bo'lmagan, lekin __hash__ kerak sinflarni
  4. field(init=False) bilan hisoblangan (eskirishi mumkin)
  5. Sirlar repr=False siz ekanini
  6. Merosdagi tartib muammolarini

Vazifa 5: NamedTuple bilan solishtirish

Bir modelni to'rt usulda yozing:

  1. Oddiy sinf
  2. NamedTuple
  3. dataclass
  4. dataclass(frozen=True, slots=True)

Solishtiring: kod hajmi, xotira, tezlik, imkoniyatlar.

Vazifa 6: JSON serializator

  1. Har dataclass ni JSON ga o'girsin
  2. Ichma-ich dataclass larni ham
  3. datetime, Decimal, Enum, set ni qo'llab-quvvatlasin
  4. field(metadata={"json": "boshqa_nom"}) — nom o'zgartirish
  5. repr=False maydonlarni chiqarmasin
  6. Teskari: JSON dan dataclass yaratish

Vazifa 7: O'ylash

Nega dataclass tur tekshiruvini qilmaydi — tur ko'rsatkichlari majburiy bo'lsa ham?

Javob

Chunki tur ko'rsatkichlari Pythonda hech qachon ish vaqtida tekshirilmaydi (7.14-dars), va dataclass bu qoidani buzmasligi kerak.

1. Tur ko'rsatkichi — hujjat

python
@dataclass
class Nuqta:
    x: int
    y: int

n = Nuqta("emas", [1, 2])       # ✅ ishlaydi!
n.x                             # 'emas'

dataclass x: int ni faqat maydon e'loni sifatida ishlatadi — turning o'zi e'tiborga olinmaydi (deyarli).

2. Nega tekshirmaydi

a) Falsafaga zid

Python tur ko'rsatkichlarini hech qayerda ish vaqtida tekshirmaydi:

python
def f(x: int) -> str:
    return x * 2

f("ab")                         # ✅ 'abab'

Agar dataclass tekshirsa, bu istisno bo'lardi va chalkashlik tug'dirardi:

"Nega dataclass tekshiradi, oddiy funksiya yo'q?"

b) Narxi katta

python
@dataclass
class A:
    m: dict[str, list[tuple[int, str]]]

Bu turni to'liq tekshirish uchun butun tuzilmani aylanib chiqish kerak:

python
isinstance(m, dict)
all(isinstance(k, str) for k in m)
all(isinstance(v, list) for v in m.values())
all(isinstance(t, tuple) and len(t) == 2 and
    isinstance(t[0], int) and isinstance(t[1], str)
    for v in m.values() for t in v)

Har obyekt yaratishda O(n) yoki yomonroq.

c) Ba'zi turlarni tekshirib bo'lmaydi

python
from typing import Callable, TypeVar, Protocol, Any

@dataclass
class A:
    f: Callable[[int], str]     # ⚠️ imzoni tekshirib bo'lmaydi
    x: Any                      # ⚠️ nima tekshirish kerak?
    y: "KelajakdaAniqlanadi"    # ⚠️ satr — hali mavjud emas
    z: TypeVar("T")             # ⚠️

d) Duck typing buzilardi

python
@dataclass
class A:
    lst: list[int]

A((1, 2, 3))                    # tuple — ⚠️ tekshiruv rad etardi
A(range(3))                     # range — ⚠️ ham

Pythonda list[int] odatda "iteratsiya qilinadigan sonlar" degani, qat'iy list emas.

3. Nima uchun tur ko'rsatkichi MAJBURIY

Agar tekshirilmasa, nega kerak?

Uch sabab:

a) Maydonni ajratish

python
@dataclass
class A:
    x: int = 5                  # ⭐ MAYDON
    y = 10                      # ⭐ oddiy sinf atributi

Tur ko'rsatkichi — sintaktik signal: "bu maydon". Boshqa yo'l yo'q edi.

b) __annotations__ dan o'qish

python
@dataclass
class A:
    x: int
    y: str = "a"

A.__annotations__               # {'x': int, 'y': str}

dataclass aynan shu lug'atni o'qiydi:

python
# dataclasses.py dan (soddalashtirilgan):
for nom, tur in cls.__dict__.get('__annotations__', {}).items():
    maydonlar.append(_get_field(cls, nom, tur))

c) mypy uchun

python
@dataclass
class Nuqta:
    x: int
    y: int

Nuqta("a", "b")
# mypy: Argument 1 to "Nuqta" has incompatible type "str"; expected "int"

Statik tekshiruv ishlaydi — dataclass mypy uchun __init__ imzosini beradi.

4. ClassVar va InitVar — istisnolar

Ikki holatda dataclass turni haqiqatan tekshiradi:

python
from typing import ClassVar
from dataclasses import InitVar

@dataclass
class A:
    x: ClassVar[int] = 0        # ⭐ maydon EMAS
    y: InitVar[str] = ""        # ⭐ __post_init__ ga
    z: int = 0                  # oddiy maydon

Lekin bu ham matn darajasida:

python
# dataclasses.py:
def _is_classvar(a_type, typing):
    return (a_type is typing.ClassVar
            or (type(a_type) is typing._GenericAlias
                and a_type.__origin__ is typing.ClassVar))

def _is_type(annotation, cls, a_module, a_type, is_type_predicate):
    # ⚠️ Satr annotatsiyalar uchun REGEX bilan tekshiriladi!
    match = _MODULE_IDENTIFIER_RE.match(annotation)
    ...

from __future__ import annotations bilan barcha annotatsiyalar satr bo'ladi, va dataclass "ClassVar[int]" satrini regex bilan tekshiradi. Bu — mo'rt, lekin ishlaydi.

5. O'zgaruvchan sukut — yagona haqiqiy tekshiruv

python
@dataclass
class A:
    x: list = []                # ❌ ValueError

Bu qiymat tekshiruvi, tur emas:

python
# dataclasses.py:
if f.default.__class__.__hash__ is None:
    raise ValueError(f'mutable default {type(f.default)} for field '
                     f'{f.name} is not allowed: use default_factory')

Mezon — __hash__ bor-yo'qligi. list, dict, set da __hash__ = None.

Bu to'liq emas:

python
@dataclass
class A:
    x: object = SomeObject()    # ⚠️ hashable, lekin o'zgaruvchan bo'lishi mumkin

6. Muqobillar — tekshiradiganlar

Kutubxona Tekshiruv Tezlik Bog'liqlik
dataclasses Tez Yo'q
attrs Ixtiyoriy (validator) Tez Kichik
pydantic To'liq + o'girish Sekinroq (v2 da Rust)
msgspec To'liq Eng tez
beartype Dekorator Tez

pydantic misoli:

python
from pydantic import BaseModel, Field, field_validator

class Nuqta(BaseModel):
    x: int
    y: int = Field(ge=0, le=100)

    @field_validator("x")
    @classmethod
    def x_musbat(cls, v):
        if v < 0: raise ValueError("musbat bo'lsin")
        return v


Nuqta(x="5", y=10)              # ✅ "5" → 5 O'GIRILADI
Nuqta(x="abc", y=10)            # ❌ ValidationError
Nuqta(x=1, y=200)               # ❌ ValidationError

pydantic o'giradi ham — bu tashqi ma'lumot (JSON, form) uchun juda foydali.

attrs misoli:

python
import attrs

@attrs.define
class Nuqta:
    x: int = attrs.field(validator=attrs.validators.instance_of(int))
    y: int = attrs.field(validator=[
        attrs.validators.instance_of(int),
        attrs.validators.ge(0),
    ])

7. Qachon qaysi

python
# ⭐ 1. Ichki ma'lumot, tur allaqachon to'g'ri
@dataclass
class Nuqta:
    x: float
    y: float

# ⭐ 2. Tashqi ma'lumot (API, JSON, form)
class NuqtaSorov(BaseModel):        # pydantic
    x: float
    y: float

# ⭐ 3. Qo'lda tekshiruv yetsa
@dataclass
class Nuqta:
    x: float
    y: float
    def __post_init__(self):
        if not isinstance(self.x, (int, float)):
            raise TypeError(...)

# ⭐ 4. Juda ko'p obyekt, tezlik muhim
@dataclass(frozen=True, slots=True)
class Nuqta:
    x: float
    y: float

8. Tezlik farqi

python
import timeit

# dataclass
timeit.timeit(lambda: Nuqta(1.0, 2.0), number=1_000_000)
# ~0.35s

# pydantic v2
timeit.timeit(lambda: NuqtaModel(x=1.0, y=2.0), number=1_000_000)
# ~1.2s   (v1 da ~4s edi)

# oddiy tuple
timeit.timeit(lambda: (1.0, 2.0), number=1_000_000)
# ~0.05s

dataclass — tekshiruvsiz, shuning uchun tez. pydantic v2 Rust da yozilgan va ancha tezlashdi, lekin hali ham 3× sekinroq.

9. Kelajak

PEP 681 3.11-bob — @dataclass_transform:

python
from typing import dataclass_transform

@dataclass_transform()
def mening_modelim(cls): ...

Bu mypy ga "bu dekorator dataclass kabi ishlaydi" deb aytadi. pydantic, attrs, SQLAlchemy shuni ishlatadi.

PEP 649 3.14-bob — kechiktirilgan annotatsiya hisoblash. Bu dataclass uchun ClassVar aniqlashni soddalashtiradi (regex o'rniga haqiqiy obyekt).

10. Xulosa

dataclass tur tekshirmaydi, chunki:

  1. Python falsafasi — tur ko'rsatkichlari ish vaqtida tekshirilmaydi
  2. Narxi — chuqur tekshiruv O(n) yoki yomonroq
  3. Mumkin emas — Callable, Any, TypeVar, oldinga havolalar
  4. Duck typing — tuple list[int] o'rniga o'tishi mumkin
  5. Standart kutubxona — bog'liqliksiz va tez bo'lishi kerak

Va tur ko'rsatkichi majburiy, chunki u:

  1. Maydonni oddiy atributdan ajratadi
  2. __annotations__ orqali dataclass ga ma'lumot beradi
  3. mypy uchun __init__ imzosini yaratadi

Umumiy naqsh: Python qatlamlarni ajratadi — dataclass kod generatsiyasi, mypy statik tekshiruv, pydantic ish vaqtidagi validatsiya. Har biri o'z ishini qiladi va siz kerakli qatlamni tanlaysiz.

Nimani mustahkamlaydi: 2.1, 2.8-bo'limlar.


Xulosa

Bu darsda dataclass ni to'liq o'rgandik.

Eng muhim uch fikr:

  1. dataclass — kod generatori, tur tekshiruvchisi emas. U __annotations__ dagi maydonlarni o'qib __init__, __repr__, __eq__ (va sozlamalarga qarab boshqalarni) yozadi. Tur ko'rsatkichi majburiy, lekin faqat maydonni ajratish uchun — ish vaqtida tekshirilmaydi. Tekshiruv kerak bo'lsa — __post_init__ yoki pydantic.

  2. O'zgaruvchan sukut tuzog'i taqiqlangan. teglar: list = [] — ValueError sinf yaratish paytida. field(default_factory=list) ishlating. Bu — dataclass ning oddiy sinfga nisbatan eng katta amaliy afzalligi: 8.3-darsda ko'rgan jimgina tuzoq mumkin emas bo'lib qoladi.

  3. __hash__ faqat frozen=True bilan keladi. Sukut bo'yicha (eq=True, frozen=False) __hash__ None bo'ladi — obyekt to'plamga solinmaydi. Bu ataylab: o'zgaruvchan hashable obyekt to'plamda topilmay qoladi (8.12-dars). Ma'lumot obyektlari uchun frozen=True, slots=True — sukut tanlov bo'lsin.

Keyingi darsda abstrakt asos sinflarni (ABC) ko'ramiz: @abstractmethod, collections.abc, register() va interfeys loyihalash.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.17-dars: dataclass — IlmHamroh