Dataclasses — @dataclass, field, frozen, __post_init__
Muammodan boshlaylik
Oddiy "ma'lumot saqlovchi" klass:
class Mahsulot:
def __init__(self, nomi, narx, soni=0, teglar=None):
self.nomi = nomi
self.narx = narx
self.soni = soni
self.teglar = teglar if teglar is not None else []
def __repr__(self):
return f"Mahsulot(nomi={self.nomi!r}, narx={self.narx!r}, soni={self.soni!r}, teglar={self.teglar!r})"
def __eq__(self, boshqa):
if not isinstance(boshqa, Mahsulot):
return NotImplemented
return (self.nomi, self.narx, self.soni, self.teglar) == (boshqa.nomi, boshqa.narx, boshqa.soni, boshqa.teglar)
To'rtta maydon — 15 qator takrorlanuvchi kod. Har bir maydon nomi to'rt marta yozilgan. Yangi maydon qo'shilsa — to'rt joyda o'zgartirish, bittasini unutish oson.
@dataclass
from dataclasses import dataclass, field
@dataclass
class Mahsulot:
nomi: str
narx: int
soni: int = 0
teglar: list[str] = field(default_factory=list)
Bu 5 qator yuqoridagi 15 qatorning hammasini avtomatik yaratadi:
m = Mahsulot("Choynak", 120000, teglar=["idish"])
print(m) # Mahsulot(nomi='Choynak', narx=120000, soni=0, teglar=['idish'])
print(m == Mahsulot("Choynak", 120000, teglar=["idish"])) # True
m.soni += 5
@dataclass maydonlarni type hints (48-dars) dan oladi va yaratadi:
__init__— barcha maydonlar bilan__repr__— o'qishli ko'rinish__eq__— maydonlar bo'yicha solishtirish
Hayotiy o'xshatma
Anketa blankasi bosmaxonasi. Siz faqat maydonlar ro'yxatini berasiz: "Ism, Yosh, Telefon". Bosmaxona o'zi chiroyli blankani, chiziqlarni va sarlavhalarni yaratadi. Har bir yangi anketa uchun chizg'ich bilan qo'lda chizish shart emas.
Standart qiymatlar va field
@dataclass
class Buyurtma:
mijoz: str
summa: int
holat: str = "yangi"
mahsulotlar: list[str] = field(default_factory=list)
izoh: str = field(default="", repr=False)
[!WARNING] O'zgaruvchan standart qiymat to'g'ridan-to'g'ri yozilsa —
@dataclassxato beradi:mahsulotlar: list = []→ValueError: mutable default <class 'list'> for field mahsulotlar is not allowed: use default_factory. Bu 18-darsdagi tuzoqdan himoya: har bir ob'ekt uchun yangi ro'yxat —field(default_factory=list).
field() parametri |
Vazifasi |
|---|---|
default |
Standart qiymat |
default_factory |
Har safar yangi qiymat yaratuvchi funksiya (list, dict, datetime.now) |
repr=False |
print da ko'rsatmaslik (parol, katta ma'lumot) |
compare=False |
== da hisobga olmaslik |
init=False |
__init__ parametri emas (ichkarida hisoblanadi) |
Standart qiymatsiz maydonlar — oldin, standart qiymatlilar — keyin (funksiya parametrlari kabi, 18-dars).
__post_init__ — qo'shimcha tekshiruv va hisob
__init__ avtomatik yaratiladi — tekshiruvni qayerga yozish kerak?
from dataclasses import dataclass, field
@dataclass
class Mahsulot:
nomi: str
narx: int
chegirma: float = 0.0
yakuniy_narx: int = field(init=False)
def __post_init__(self):
self.nomi = self.nomi.strip()
if self.narx < 0:
raise ValueError("Narx manfiy bo'lishi mumkin emas")
if not 0 <= self.chegirma <= 100:
raise ValueError("Chegirma 0–100 oralig'ida bo'lsin")
self.yakuniy_narx = round(self.narx * (1 - self.chegirma / 100))
m = Mahsulot(" Choynak ", 120000, chegirma=10)
print(m) # Mahsulot(nomi='Choynak', narx=120000, chegirma=10, yakuniy_narx=108000)
Mahsulot("Kosa", -5) # ValueError
__post_init__ — avtomatik __init__ tugagandan keyin chaqiriladi.
frozen — o'zgarmas
@dataclass(frozen=True)
class Nuqta:
x: float
y: float
p = Nuqta(3, 4)
p.x = 10 # ❌ FrozenInstanceError: cannot assign to field 'x'
joylar = {Nuqta(0, 0): "markaz"} # ✅ hashable — dictionary kaliti bo'la oladi
- Yaratilgandan keyin o'zgartirib bo'lmaydi (tuple kabi, 12-dars)
__hash__avtomatik — set va dictionary kaliti sifatida ishlatsa bo'ladi- "Yangilash" — yangi nusxa:
from dataclasses import replace
p2 = replace(p, x=10) # Nuqta(x=10, y=4)
Koordinatalar, pul summalari, sozlamalar kabi qiymat ob'ektlari uchun — frozen=True.
Boshqa opsiyalar
@dataclass(order=True)
class Talaba:
reyting: float
ism: str = field(compare=False)
talabalar = [Talaba(4.5, "Aziz"), Talaba(4.9, "Lola"), Talaba(4.1, "Bobur")]
print([t.ism for t in sorted(talabalar, reverse=True)]) # ['Lola', 'Aziz', 'Bobur']
| Opsiya | Nima qiladi |
|---|---|
order=True |
<, >, <=, >= — maydonlar tartibida |
frozen=True |
O'zgarmas + hashable |
slots=True (3.10+) |
Kamroq xotira, tezroq atributlar |
kw_only=True (3.10+) |
Barcha maydonlar faqat nom bilan: Mahsulot(nomi=..., narx=...) |
Metodlar va meros
Dataclass — oddiy klass: metodlar, property lar, meros — hammasi ishlaydi:
@dataclass
class BankHisobi:
egasi: str
balans: int = 0
def pul_qosh(self, summa: int) -> None:
self.balans += summa
@property
def formatlangan(self) -> str:
return f"{self.balans:,} so'm"
@dataclass
class OmonatHisobi(BankHisobi):
stavka: float = 0.14
Dictionary va JSON ga
from dataclasses import asdict, astuple
import json
m = Mahsulot("Choynak", 120000, teglar=["idish"])
print(asdict(m)) # {'nomi': 'Choynak', 'narx': 120000, 'soni': 0, 'teglar': ['idish']}
print(json.dumps(asdict(m), ensure_ascii=False))
d = {"nomi": "Kosa", "narx": 30000}
m2 = Mahsulot(**d) # dictionary dan (19-dars — ** yoyish)
dataclass, namedtuple yoki pydantic?
namedtuple |
@dataclass |
pydantic.BaseModel |
|
|---|---|---|---|
| O'zgaruvchan | ❌ | ✅ (yoki frozen) |
✅ |
| Standart kutubxona | ✅ | ✅ | ❌ (tashqi) |
| Turlarni ishlash paytida tekshiradi | ❌ | ❌ | ✅ |
Tur o'girish ("25" → 25) |
❌ | ❌ | ✅ |
| Qachon | Oddiy o'zgarmas yozuv | Ilova ichidagi ma'lumot ob'ektlari | Tashqi ma'lumot: API, JSON, formalar |
[!INFO]
@dataclasstype hints ni tekshirmaydi:Mahsulot("Choynak", "arzon")xatosiz yaratiladi. Tashqi manbadan kelgan ma'lumotni tekshirish kerak bo'lsa —__post_init__da tekshiruv yokipydantic(FastAPI ichida ishlatiladigan kutubxona).
Tez-tez uchraydigan xatolar
Type hint siz maydon:
@dataclass
class A:
x = 5 # ❌ maydon emas — oddiy class atributi, __init__ ga kirmaydi
x: int = 5 # ✅
O'zgaruvchan default — field(default_factory=list).
Standart qiymatli maydondan keyin standart qiymatsiz:
@dataclass
class B:
a: int = 0
b: str # ❌ TypeError: non-default argument 'b' follows default argument
Xulosa
@dataclass — ma'lumot saqlovchi klasslar uchun takrorlanuvchi kodni avtomatik yaratadi.
Eslab qoling:
@dataclass+ type hints →__init__,__repr__,__eq__- Ro'yxat/dict standart qiymat —
field(default_factory=list) field(repr=False, compare=False, init=False)__post_init__— tekshiruv va hisoblangan maydonlarfrozen=True— o'zgarmas, hashable;replace()— yangi nusxaorder=True,slots=True,kw_only=Trueasdict()— JSON uchun; turlar tekshirilmaydi — kerak bo'lsa pydantic
Keyingi dars
50-dars: logging — level, handler, formatter, haqiqiy loyihada qanday ishlatiladi