Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. __init__ ning vazifasi
- 2.2. Sukut argument tuzog'i — sinflarda
- 2.3. Atributlarni e'lon qilish
- 2.4. Invariant — qaerda tekshirish
- 2.5. Hosila atributlar
- 2.6. Fabrika metodlari
- 2.7. dataclass — avtomatik __init__
- 2.8. __init__ va meros
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Sukut argument tuzoqlari
- Misol 2 — Invariant loyihalash
- Misol 3 — Hosila atributlar
- Misol 4 — Amaliy: konfiguratsiya sinfi
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
8.3-dars: __init__ va atributlar
8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 3-dars
1. Kirish va motivatsiya
Ikki __init__, bir xil maqsad:
class Buyurtma:
def __init__(self, mahsulotlar=[], chegirma=0, mijoz=None):
self.mahsulotlar = mahsulotlar
self.chegirma = chegirma
self.mijoz = mijozclass Buyurtma:
def __init__(
self,
mijoz: str,
mahsulotlar: list[str] | None = None,
chegirma: float = 0.0,
):
if not mijoz.strip():
raise ValueError("Mijoz ko'rsatilsin")
if not 0 <= chegirma <= 100:
raise ValueError(f"Chegirma 0–100: {chegirma}")
self.mijoz = mijoz.strip()
self.mahsulotlar = list(mahsulotlar or [])
self.chegirma = chegirma
self._yaratilgan = datetime.now()
self._holat = "yangi"Birinchisidagi to'rt xato:
b1 = Buyurtma()
b1.mahsulotlar.append("non")
b2 = Buyurtma()
b2.mahsulotlar # ['non'] ⚠️ umumiy sukut ro'yxat!
b3 = Buyurtma(chegirma=500) # ⚠️ tekshirilmadi
b4 = Buyurtma(mijoz="") # ⚠️ bo'sh mijoz
b5 = Buyurtma(["non"]) # ⚠️ mijoz o'rniga ro'yxatBu darsda:
- Sukut argument tuzog'i — sinflarda ham
- Atribut nomlash va e'lon qilish
- Invariant va uni qaerda tekshirish
- Hosila atributlar va
@property __post_init__, fabrika metodlari- Nusxa atributlarini bir joyda e'lon qilish
2. Nazariya — chuqur tushuntirish
2.1. __init__ ning vazifasi
def __init__(self, ...) -> None:
"""Obyektni ISHLATISHGA TAYYOR holatga keltiradi."""To'rt vazifa:
- Argumentlarni tekshirish — noto'g'ri obyekt yaratilmasin
- Normallashtirish —
strip(), tur o'girish, nusxa olish - Atributlarni o'rnatish — barchasi shu yerda
- Boshlang'ich holat — hisoblagichlar, keshlar, bayroqlar
__init__ da BO'LMASLIGI kerak:
def __init__(self, url):
self.malumot = tarmoqdan_yukla(url) # ⚠️ sekin, xato berishi mumkin
self.fayl = open("a.txt") # ⚠️ resurs — with kerak
print("Yaratildi") # ⚠️ yon ta'sir
global JAMI; JAMI += 1 # ⚠️ global holatOg'ir ish uchun fabrika metodi yoki kechiktirilgan yuklash:
class Malumot:
def __init__(self, xom: dict):
self.xom = xom # tez, ishonchli
@classmethod
def urldan(cls, url: str) -> "Malumot": # ✅ aniq
return cls(tarmoqdan_yukla(url))2.2. Sukut argument tuzog'i — sinflarda
7.3-darsda ko'rgan tuzoq bu yerda ikki xil ko'rinishda uchraydi:
A) __init__ sukut argumenti (funksiya bilan bir xil):
class A:
def __init__(self, lst=[]): # ⚠️ BIR MARTA yaratiladi
self.lst = lst
a, b = A(), A()
a.lst.append(1)
b.lst # [1] ⚠️
a.lst is b.lst # TrueB) Sinf atributi (8.2-darsda ko'rgan):
class A:
lst = [] # ⚠️ hamma nusxaga umumiy
a, b = A(), A()
a.lst.append(1)
b.lst # [1] ⚠️Ikkalasining yechimi bir xil:
class A:
def __init__(self, lst: list | None = None):
self.lst = list(lst) if lst is not None else [] list(lst) — nusxa oladi. Bu ikkinchi himoya:
class B:
def __init__(self, lst):
self.lst = lst # ⚠️ tashqi ro'yxatga havola
tashqi = [1, 2]
b = B(tashqi)
tashqi.append(3)
b.lst # [1, 2, 3] ⚠️ obyekt ichi o'zgardiQachon nusxa olish kerak:
| Holat | Nusxa | Sabab |
|---|---|---|
| Ro'yxat/lug'at/to'plam saqlaymiz | Tashqi o'zgarishdan himoya | |
| Faqat o'qiymiz va qaytarmaymiz | Ixtiyoriy | |
| Juda katta konteyner | Xotira; hujjatda ayting | |
O'zgarmas (tuple, frozenset) |
Kerak emas |
2.3. Atributlarni e'lon qilish
Barcha nusxa atributlari __init__ da paydo bo'lsin:
class Yomon: # ⚠️
def __init__(self, x):
self.x = x
def hisobla(self):
self.natija = self.x * 2 # ⚠️ atribut shu yerda tug'ildi
y = Yomon(5)
y.natija # AttributeError — hali chaqirilmaganclass Yaxshi: # ✅
def __init__(self, x):
self.x = x
self.natija: int | None = None # aniq e'lon
def hisobla(self):
self.natija = self.x * 2Foydasi: IDE biladi, mypy biladi, o'quvchi biladi.
Nomlash:
| Prefiks | Ma'nosi | Misol |
|---|---|---|
nom |
Ommaviy | self.ism |
_nom |
Ichki (kelishuv) | self._kesh |
__nom |
Nom buzilishi (8.10) | self.__sirli |
NOM |
Konstanta (sinf darajasida) | MAKS = 100 |
2.4. Invariant — qaerda tekshirish
Invariant — obyekt hayoti davomida doim rost bo'lishi kerak bo'lgan shart.
class Hisob:
"""Invariant: balans >= 0."""
def __init__(self, balans: float = 0):
if balans < 0: # 1️⃣ yaratishda
raise ValueError("Manfiy bo'lmasin")
self._balans = balans
def yech(self, summa: float) -> None:
if summa <= 0:
raise ValueError("Musbat bo'lsin")
if summa > self._balans: # 2️⃣ har o'zgarishda
raise ValueError("Mablag' yetarli emas")
self._balans -= summaUch qoida:
__init__da tekshiring — noto'g'ri obyekt umuman yaratilmasin- Har o'zgartiruvchi metodda tekshiring — buzilmasin
- Ichki holatni himoyalang —
_balansga to'g'ridan-to'g'ri yozilmasin
Tekshiruvni takrorlamang — bitta joyga yig'ing:
class Hisob:
def __init__(self, balans=0):
self._balans = 0
self.qosh(balans) # ✅ qayta ishlatildi
def qosh(self, summa):
if summa < 0:
raise ValueError("Manfiy bo'lmasin")
self._balans += summa2.5. Hosila atributlar
Hisoblanadigan qiymatni saqlamang:
class Togriturtburchak:
def __init__(self, a, b):
self.a, self.b = a, b
self.yuza = a * b # ⚠️ eskiradi!
t = Togriturtburchak(3, 4)
t.a = 10
t.yuza # 12 ⚠️ noto'g'ri @property bilan har safar hisoblang:
class Togriturtburchak:
def __init__(self, a, b):
self.a, self.b = a, b
@property
def yuza(self):
return self.a * self.b # ✅ doim to'g'riQachon saqlash o'rinli:
from functools import cached_property
class Hujjat:
def __init__(self, matn):
self.matn = matn # o'zgarmasligi kutiladi
@cached_property
def sozlar(self):
"""Bir marta hisoblanadi, keyin keshdan."""
return self.matn.split() cached_property faqat asos o'zgarmasa xavfsiz.
2.6. Fabrika metodlari
__init__ — bitta yo'l. Boshqa yo'llar uchun @classmethod:
class Sana:
def __init__(self, kun: int, oy: int, yil: int):
...
@classmethod
def satrdan(cls, s: str) -> "Sana":
kun, oy, yil = map(int, s.split("."))
return cls(kun, oy, yil)
@classmethod
def bugun(cls) -> "Sana":
d = date.today()
return cls(d.day, d.month, d.year)
Sana(15, 3, 2026)
Sana.satrdan("15.03.2026")
Sana.bugun() Nega fabrika, __init__ da tur tekshiruvi emas:
# ⚠️ Yomon
def __init__(self, malumot):
if isinstance(malumot, str):
...
elif isinstance(malumot, tuple):
...
elif isinstance(malumot, date):
...
# ✅ Yaxshi — har yo'l alohida nom, alohida hujjat
Sana.satrdan(...)
Sana.tupledan(...)
Sana.datedan(...)Fabrika metodlari 8.16-darsda batafsil.
2.7. dataclass — avtomatik __init__
from dataclasses import dataclass, field
@dataclass
class Nuqta:
x: float
y: float = 0.0
teglar: list[str] = field(default_factory=list) # ⭐ sukut tuzog'i yo'q
def __post_init__(self):
"""⭐ __init__ dan keyin — tekshiruv shu yerda."""
if self.x < 0:
raise ValueError("x manfiy bo'lmasin")Bepul olinadi: __init__, __repr__, __eq__.
field(default_factory=...) — majburiy:
@dataclass
class A:
lst: list = [] # ❌ ValueError yaratishda
lst: list = field(default_factory=list) # ✅ dataclass sukut tuzog'ini taqiqlaydi — bu uning katta afzalligi.
To'liq dataclass — 8.17-darsda.
2.8. __init__ va meros
class Ota:
def __init__(self, a):
self.a = a
class Bola(Ota):
def __init__(self, a, b):
super().__init__(a) # ⭐ ota-sinfni chaqirish
self.b = b super().__init__() unutish — eng ko'p uchraydigan meros xatosi:
class Bola(Ota):
def __init__(self, a, b):
self.b = b # ⚠️ self.a yo'q
Bola(1, 2).a # AttributeErrorsuper() va MRO — 8.7 va 8.8-darslarda.
3. Tez ma'lumotnoma
__init__ shabloni
class Nom:
KONSTANTA = 10
def __init__(
self,
majburiy: str,
ixtiyoriy: int = 0,
konteyner: list[str] | None = None, # ⭐ None, [] emas
) -> None:
# 1. Tekshirish
if not majburiy.strip():
raise ValueError(...)
# 2. Normallashtirish
self.majburiy = majburiy.strip()
# 3. Nusxa olish
self.konteyner = list(konteyner or [])
# 4. Ichki holat
self._kesh: dict = {}
self._holat = "yangi"Sukut tuzoqlari
def __init__(self, lst=[]): ❌ bir marta yaratiladi
class A: lst = [] ❌ hamma nusxaga umumiy
def __init__(self, lst=None): ✅
self.lst = list(lst or []) ✅ nusxa ham oladi
@dataclass
lst: list = [] ❌ ValueError
lst: list = field(default_factory=list) ✅Invariant
1. __init__ da tekshiring noto'g'ri obyekt yaratilmasin
2. Har o'zgartirishda buzilmasin
3. Ichkarini himoyalang _ prefiks, property
4. Tekshiruvni bitta joyga __init__ metodni chaqirsinHosila qiymat
self.yuza = a * b ⚠️ eskiradi
@property
def yuza(self): return ... ✅ doim to'g'ri
@cached_property ✅ asos o'zgarmasaFabrika
@classmethod
def satrdan(cls, s): return cls(...)4. Batafsil misollar
Misol 1 — Sukut argument tuzoqlari
"""Sinflardagi sukut qiymat tuzoqlarining barcha ko'rinishi."""
from dataclasses import dataclass, field
print("=== ⚠️ TUZOQ 1: __init__ sukut argumenti ===\n")
class Savat1:
def __init__(self, mahsulotlar=[]): # ⚠️
self.mahsulotlar = mahsulotlar
def qosh(self, m):
self.mahsulotlar.append(m)
a, b = Savat1(), Savat1()
a.qosh("non")
b.qosh("sut")
print(f" a.mahsulotlar = {a.mahsulotlar}")
print(f" b.mahsulotlar = {b.mahsulotlar}")
print(f" a.mahsulotlar is b.mahsulotlar = {a.mahsulotlar is b.mahsulotlar}")
print(f"\n Sukut obyekt id: "
f"{id(Savat1.__init__.__defaults__[0]) % 100000}")
print(f" a.mahsulotlar id: {id(a.mahsulotlar) % 100000}")
print(f" ⚠️ BIR XIL — sukut ro'yxat def paytida BIR MARTA yaratilgan")
print("\n=== ⚠️ TUZOQ 2: sinf atributi ===\n")
class Savat2:
mahsulotlar = [] # ⚠️
def qosh(self, m):
self.mahsulotlar.append(m) # ⚠️ sinf ro'yxatini o'zgartiradi
c, d = Savat2(), Savat2()
c.qosh("non")
d.qosh("sut")
print(f" c.mahsulotlar = {c.mahsulotlar}")
print(f" d.mahsulotlar = {d.mahsulotlar}")
print(f" Savat2.mahsulotlar = {Savat2.mahsulotlar}")
print(f" c.__dict__ = {c.__dict__} ← BO'SH!")
print(f"\n ⚠️ append() nusxaga YOZMAYDI — sinf ro'yxatini o'zgartiradi")
print("\n=== ⚠️ TUZOQ 3: tashqi obyektga havola ===\n")
class Savat3:
def __init__(self, mahsulotlar=None):
self.mahsulotlar = mahsulotlar or [] # ✅ tuzoq 1 hal bo'ldi
# ⚠️ lekin nusxa olinmadi
tashqi = ["non", "sut"]
e = Savat3(tashqi)
print(f" tashqi = {tashqi}")
print(f" e = Savat3(tashqi)")
print(f" e.mahsulotlar = {e.mahsulotlar}")
tashqi.append("shakar")
print(f"\n tashqi.append('shakar') dan keyin:")
print(f" e.mahsulotlar = {e.mahsulotlar} ⚠️ obyekt ichi o'zgardi!")
print(f" e.mahsulotlar is tashqi = {e.mahsulotlar is tashqi}")
print("\n=== ⚠️ TUZOQ 4: `or` bilan bo'sh qiymat ===\n")
class Sozlama1:
def __init__(self, chegara=None, teglar=None, nom=None):
self.chegara = chegara or 100 # ⚠️
self.teglar = teglar or []
self.nom = nom or "sukut"
s = Sozlama1(chegara=0, teglar=[], nom="")
print(f" Sozlama1(chegara=0, teglar=[], nom='')")
print(f" s.chegara = {s.chegara} ⚠️ 0 kutilgan edi!")
print(f" s.teglar = {s.teglar}")
print(f" s.nom = {s.nom!r} ⚠️ '' kutilgan edi!")
print(f"""
⚠️ SABAB: `or` FALSY qiymatlarni almashtiradi:
0, 0.0, "", [], {{}}, set(), None, False
✅ YECHIM: `is None` bilan aniq tekshirish
""")
class Sozlama2:
def __init__(self, chegara=None, teglar=None, nom=None):
self.chegara = 100 if chegara is None else chegara # ✅
self.teglar = [] if teglar is None else list(teglar) # ✅
self.nom = "sukut" if nom is None else nom # ✅
s2 = Sozlama2(chegara=0, teglar=[], nom="")
print(f" Sozlama2(chegara=0, teglar=[], nom='')")
print(f" s2.chegara = {s2.chegara} ✅")
print(f" s2.nom = {s2.nom!r} ✅")
print("\n=== ✅ TO'G'RI YECHIM ===\n")
class Savat:
"""Barcha tuzoqlar hal qilingan."""
MAKS = 50
def __init__(
self,
mijoz: str,
mahsulotlar: list[str] | None = None,
chegirma: float = 0.0,
) -> None:
# 1. Tekshirish
if not mijoz.strip():
raise ValueError("Mijoz nomi bo'sh bo'lmasin")
if not 0 <= chegirma <= 100:
raise ValueError(f"Chegirma 0–100 oralig'ida: {chegirma}")
# 2. Normallashtirish + 3. Nusxa olish
self.mijoz = mijoz.strip()
self._mahsulotlar: list[str] = list(mahsulotlar or []) # ⭐ nusxa
self.chegirma = chegirma
# 4. Ichki holat — hammasi shu yerda e'lon qilingan
self._yopilgan = False
self._jami_kesh: float | None = None
if len(self._mahsulotlar) > self.MAKS:
raise ValueError(f"Ko'pi bilan {self.MAKS} mahsulot")
@property
def mahsulotlar(self) -> tuple[str, ...]:
"""Nusxa — tashqaridan o'zgartirib bo'lmaydi."""
return tuple(self._mahsulotlar)
def qosh(self, m: str) -> None:
if self._yopilgan:
raise RuntimeError("Savat yopilgan")
if len(self._mahsulotlar) >= self.MAKS:
raise ValueError(f"Ko'pi bilan {self.MAKS} mahsulot")
self._mahsulotlar.append(m)
self._jami_kesh = None # kesh eskirdi
def __repr__(self):
return (f"Savat({self.mijoz!r}, {len(self._mahsulotlar)} mahsulot, "
f"chegirma={self.chegirma})")
print(f" Alohida nusxalar:")
x, y = Savat("Aziz"), Savat("Malika")
x.qosh("non")
y.qosh("sut")
print(f" x = {x}, mahsulotlar={x.mahsulotlar}")
print(f" y = {y}, mahsulotlar={y.mahsulotlar}")
print(f" ✅ ajralgan: {x.mahsulotlar != y.mahsulotlar}")
print(f"\n Tashqi o'zgarishdan himoya:")
tashqi = ["non", "sut"]
z = Savat("Bobur", tashqi)
tashqi.append("shakar")
print(f" tashqi = {tashqi}")
print(f" z.mahsulotlar = {z.mahsulotlar} ✅ o'zgarmadi")
print(f"\n Ichkarini o'zgartirishdan himoya:")
try:
z.mahsulotlar.append("qand")
except AttributeError as e:
print(f" z.mahsulotlar.append(...) → ✅ AttributeError (tuple)")
print(f"\n Invariant:")
for tavsif, f in [
("Savat('')", lambda: Savat("")),
("chegirma=200", lambda: Savat("A", chegirma=200)),
("chegirma=-5", lambda: Savat("A", chegirma=-5)),
("60 mahsulot", lambda: Savat("A", ["x"] * 60)),
("chegirma=0", lambda: Savat("A", chegirma=0)),
]:
try:
f(); n = "✅ o'tdi"
except ValueError as err:
n = f"✅ ValueError: {err}"
print(f" {tavsif:<18} {n}")
print("\n=== ⭐ dataclass tuzoqni TAQIQLAYDI ===\n")
print(""" @dataclass
class A:
lst: list = [] ❌ ValueError yaratish PAYTIDA:
mutable default <class 'list'> for
field lst is not allowed
""")
try:
@dataclass
class Xato:
lst: list = []
except ValueError as e:
print(f" Haqiqiy xato: ValueError: {str(e)[:70]}...")
@dataclass
class Togri:
nom: str
lst: list[str] = field(default_factory=list) # ✅
kesh: dict = field(default_factory=dict, repr=False) # ✅ repr da yo'q
def __post_init__(self):
if not self.nom.strip():
raise ValueError("Nom bo'sh bo'lmasin")
self.nom = self.nom.strip()
t1, t2 = Togri("A"), Togri("B")
t1.lst.append("x")
print(f"\n ✅ t1 = {t1}")
print(f" ✅ t2 = {t2}")
print(f" t1.lst is t2.lst = {t1.lst is t2.lst}")
try:
Togri(" ")
except ValueError as e:
print(f"\n __post_init__ tekshiruvi: ✅ ValueError: {e}")
print(f"""
⭐ dataclass afzalliklari:
• Sukut tuzog'ini TAQIQLAYDI (ValueError)
• __init__, __repr__, __eq__ bepul
• __post_init__ da tekshiruv
• field(repr=False) — sirlarni yashirish
""")Natijaning muhim qismi:
=== ⚠️ TUZOQ 1: __init__ sukut argumenti ===
a.mahsulotlar = ['non', 'sut']
b.mahsulotlar = ['non', 'sut']
a.mahsulotlar is b.mahsulotlar = True
=== ⚠️ TUZOQ 4: `or` bilan bo'sh qiymat ===
Sozlama1(chegara=0, teglar=[], nom='')
s.chegara = 100 ⚠️ 0 kutilgan edi!
s.nom = 'sukut' ⚠️ '' kutilgan edi!
Sozlama2(chegara=0, teglar=[], nom='')
s2.chegara = 0 ✅
=== ✅ TO'G'RI YECHIM ===
Tashqi o'zgarishdan himoya:
tashqi = ['non', 'sut', 'shakar']
z.mahsulotlar = ('non', 'sut') ✅ o'zgarmadiNima ko'rsatdi: 2.2-bo'lim.
Misol 2 — Invariant loyihalash
"""Invariantni qanday aniqlash va himoyalash."""
from datetime import date, datetime
print("=== 1. Invariantsiz sinf ===\n")
class HisobYomon:
def __init__(self, egasi, balans=0):
self.egasi = egasi
self.balans = balans
def yech(self, summa):
self.balans -= summa
h = HisobYomon("Aziz", 100)
BUZISHLAR = [
("h.yech(1000)", lambda: h.yech(1000)),
("h.balans = -999", lambda: setattr(h, "balans", -999)),
("h.yech(-50)", lambda: h.yech(-50)),
("h.egasi = None", lambda: setattr(h, "egasi", None)),
("h.balans = 'abc'", lambda: setattr(h, "balans", "abc")),
]
for kod, f in BUZISHLAR:
f()
print(f" {kod:<22} → balans={h.balans!r}, egasi={h.egasi!r}")
print(f"\n ⚠️ Hech qanday himoya yo'q — obyekt istalgan holatga tushdi")
print("\n=== 2. ✅ Invariant bilan ===\n")
class Hisob:
"""Bank hisobi.
INVARIANTLAR:
1. balans >= 0 (doim)
2. egasi — bo'sh bo'lmagan satr
3. tarix faqat o'sadi, o'chirilmaydi
4. yopilgan hisobda amal bajarilmaydi
"""
ENG_KAM_QOLDIQ = 0.0
ENG_KOP_AMAL = 1_000_000_000.0
def __init__(self, egasi: str, balans: float = 0.0) -> None:
# Barcha atributlar shu yerda e'lon qilinadi
self._egasi = ""
self._balans = 0.0
self._tarix: list[tuple[datetime, str, float, float]] = []
self._yopilgan = False
self.egasi = egasi # setter tekshiradi
if balans:
self.qosh(balans, "boshlang'ich")
# ── Xususiyatlar ──
@property
def egasi(self) -> str:
return self._egasi
@egasi.setter
def egasi(self, qiymat: str) -> None:
"""⭐ Invariant 2 — bitta joyda."""
if not isinstance(qiymat, str):
raise TypeError(f"Egasi satr bo'lsin: {type(qiymat).__name__}")
if not qiymat.strip():
raise ValueError("Egasi bo'sh bo'lmasin")
self._egasi = qiymat.strip()
@property
def balans(self) -> float:
"""⭐ Faqat o'qish — setter YO'Q."""
return self._balans
@property
def tarix(self) -> tuple:
return tuple(self._tarix)
@property
def yopilgan(self) -> bool:
return self._yopilgan
# ── Amallar ──
def _tekshir(self, summa: float) -> float:
"""⭐ Umumiy tekshiruv — takrorlanmaydi."""
if self._yopilgan:
raise RuntimeError("Hisob yopilgan")
if not isinstance(summa, (int, float)) or isinstance(summa, bool):
raise TypeError(f"Summa son bo'lsin: {type(summa).__name__}")
if summa <= 0:
raise ValueError(f"Summa musbat bo'lsin: {summa}")
if summa > self.ENG_KOP_AMAL:
raise ValueError(f"Juda katta summa: {summa}")
return float(summa)
def _yoz(self, amal: str, summa: float) -> None:
self._tarix.append((datetime.now(), amal, summa, self._balans))
def qosh(self, summa: float, izoh: str = "kirim") -> None:
summa = self._tekshir(summa)
self._balans += summa
self._yoz(izoh, summa)
def yech(self, summa: float) -> None:
summa = self._tekshir(summa)
yangi = self._balans - summa
if yangi < self.ENG_KAM_QOLDIQ: # ⭐ Invariant 1
raise ValueError(
f"Mablag' yetarli emas: {self._balans:.2f} da "
f"{summa:.2f} yechib bo'lmaydi"
)
self._balans = yangi
self._yoz("chiqim", -summa)
def otkaz(self, boshqa: "Hisob", summa: float) -> None:
"""⭐ Atomik: xato bo'lsa hech nima o'zgarmaydi."""
if boshqa is self:
raise ValueError("O'ziga o'tkazib bo'lmaydi")
self.yech(summa) # avval tekshiradi
try:
boshqa.qosh(summa, f"{self.egasi} dan")
except Exception:
self.qosh(summa, "qaytarildi") # orqaga qaytarish
raise
def yop(self) -> None:
if self._balans:
raise RuntimeError(f"Balans nol emas: {self._balans:.2f}")
self._yopilgan = True
def __repr__(self) -> str:
holat = " YOPIQ" if self._yopilgan else ""
return f"Hisob({self._egasi!r}, {self._balans:.2f}{holat})"
print(" To'g'ri ishlatish:\n")
a = Hisob("Aziz", 500_000)
b = Hisob("Malika")
a.qosh(200_000, "maosh")
a.yech(50_000)
a.otkaz(b, 100_000)
print(f" {a}")
print(f" {b}")
print(f"\n a ning tarixi:")
for vaqt, amal, summa, oldingi in a.tarix:
print(f" {vaqt:%H:%M:%S} {amal:<18} {summa:>+14,.0f} "
f"(oldin: {oldingi:>12,.0f})")
print(f"\n Invariant himoyasi:\n")
HIMOYA = [
("a.yech(99_999_999)", lambda: a.yech(99_999_999)),
("a.yech(-100)", lambda: a.yech(-100)),
("a.yech(0)", lambda: a.yech(0)),
("a.yech('abc')", lambda: a.yech("abc")),
("a.yech(True)", lambda: a.yech(True)),
("a.balans = 999", lambda: setattr(a, "balans", 999)),
("a.egasi = ''", lambda: setattr(a, "egasi", "")),
("a.egasi = 42", lambda: setattr(a, "egasi", 42)),
("a.tarix.clear()", lambda: a.tarix.clear()),
("a.otkaz(a, 100)", lambda: a.otkaz(a, 100)),
("a.yop() (balans>0)", lambda: a.yop()),
]
for kod, f in HIMOYA:
try:
f()
natija = "⚠️ O'TDI"
except Exception as e:
natija = f"✅ {type(e).__name__}: {str(e)[:44]}"
print(f" {kod:<24} {natija}")
print(f"\n Yakuniy holat: {a} ← invariant buzilmadi")
print("\n\n=== 3. Atomik amal ===\n")
class NosozHisob(Hisob):
"""Test uchun — qosh() doim xato beradi."""
def qosh(self, summa, izoh="kirim"):
if izoh != "qaytarildi":
raise RuntimeError("Tarmoq xatosi")
super().qosh(summa, izoh)
c = Hisob("Bobur", 300_000)
n = NosozHisob("Nosoz")
print(f" Oldin: {c}")
try:
c.otkaz(n, 100_000)
except RuntimeError as e:
print(f" Xato: RuntimeError: {e}")
print(f" Keyin: {c} ✅ pul qaytarildi")
print(f"""
⭐ ATOMIKLIK: amal to'liq bajariladi yoki umuman bajarilmaydi.
try:
boshqa.qosh(summa)
except Exception:
self.qosh(summa, "qaytarildi") # orqaga qaytarish
raise
""")
print("=== 4. Invariantni hujjatlash ===\n")
print(f" {Hisob.__doc__}")
print("""
⭐ INVARIANTNI ANIQLASH SAVOLLARI:
1. Bu atribut qanday qiymatlarni QABUL QILMASLIGI kerak?
→ balans < 0, egasi = ""
2. Ikki atribut o'rtasida bog'liqlik bormi?
→ boshlanish <= tugash
3. Qaysi amal qaysi holatda TAQIQLANGAN?
→ yopilgan hisobda yech()
4. Nimani tashqaridan o'zgartirib BO'LMASLIGI kerak?
→ tarix, balans (to'g'ridan-to'g'ri)
5. Xato o'rtada sodir bo'lsa nima bo'ladi?
→ orqaga qaytarish (atomiklik)
""")Natijaning muhim qismi:
=== 1. Invariantsiz sinf ===
h.yech(1000) → balans=-900, egasi='Aziz'
h.balans = -999 → balans=-999, egasi='Aziz'
h.balans = 'abc' → balans='abc', egasi=None
=== 2. ✅ Invariant bilan ===
Hisob('Aziz', 550000.00)
Hisob('Malika', 100000.00)
Invariant himoyasi:
a.yech(99_999_999) ✅ ValueError: Mablag' yetarli emas
a.yech(-100) ✅ ValueError: Summa musbat bo'lsin: -100
a.yech(True) ✅ TypeError: Summa son bo'lsin: bool
a.balans = 999 ✅ AttributeError: property 'balans' of 'Hisob' object has no s
a.tarix.clear() ✅ AttributeError: 'tuple' object has no...
a.otkaz(a, 100) ✅ ValueError: O'ziga o'tkazib bo'lmaydi
=== 3. Atomik amal ===
Oldin: Hisob('Bobur', 300000.00)
Xato: RuntimeError: Tarmoq xatosi
Keyin: Hisob('Bobur', 300000.00) ✅ pul qaytarildiNima ko'rsatdi: 2.4-bo'lim.
Misol 3 — Hosila atributlar
"""Hisoblanadigan qiymatlarni qanday saqlash (yoki saqlamaslik)."""
import time
from functools import cached_property
print("=== ⚠️ 1. Saqlangan hosila qiymat eskiradi ===\n")
class TogriturtburchakYomon:
def __init__(self, a, b):
self.a, self.b = a, b
self.yuza = a * b # ⚠️
self.perimetr = 2 * (a + b) # ⚠️
t = TogriturtburchakYomon(3, 4)
print(f" Boshida: a={t.a}, b={t.b}, yuza={t.yuza}, perimetr={t.perimetr}")
t.a = 10
print(f" a=10: a={t.a}, b={t.b}, yuza={t.yuza} ⚠️, "
f"perimetr={t.perimetr} ⚠️")
print(f" Kutilgan: yuza=40, perimetr=28")
print("\n=== ✅ 2. @property — har safar hisoblanadi ===\n")
class Togriturtburchak:
def __init__(self, a: float, b: float):
self.a, self.b = a, b
@property
def yuza(self) -> float:
return self.a * self.b
@property
def perimetr(self) -> float:
return 2 * (self.a + self.b)
@property
def diagonal(self) -> float:
return (self.a ** 2 + self.b ** 2) ** 0.5
@property
def kvadratmi(self) -> bool:
return self.a == self.b
def __repr__(self):
return f"Togriturtburchak({self.a}, {self.b})"
t = Togriturtburchak(3, 4)
print(f" {t}: yuza={t.yuza}, perimetr={t.perimetr}, "
f"diagonal={t.diagonal:.2f}")
t.a = 10
print(f" a=10 → yuza={t.yuza} ✅, perimetr={t.perimetr} ✅")
t.a = t.b = 5
print(f" a=b=5 → kvadratmi={t.kvadratmi} ✅")
print(f"\n ⭐ Chaqiruvchi kod o'zgarmaydi:")
print(f" t.yuza ← metod emas, atribut kabi")
print(f" t.yuza() ❌ TypeError")
print("\n=== 3. @cached_property — bir marta hisoblanadi ===\n")
class Hujjat:
def __init__(self, matn: str):
self.matn = matn
@property
def sozlar_sekin(self) -> list[str]:
"""Har safar qayta hisoblanadi."""
time.sleep(0.02) # og'ir ishni taqlid qilamiz
return self.matn.split()
@cached_property
def sozlar(self) -> list[str]:
"""⭐ Bir marta, keyin keshdan."""
time.sleep(0.02)
return self.matn.split()
@cached_property
def statistika(self) -> dict:
return {
"sozlar": len(self.sozlar),
"noyob": len(set(self.sozlar)),
"belgilar": len(self.matn),
}
h = Hujjat("Python juda kuchli va oddiy dasturlash tili " * 200)
print(f" @property (har safar hisoblanadi):")
boshlanish = time.perf_counter()
for _ in range(5):
_ = h.sozlar_sekin
print(f" 5 marta: {time.perf_counter() - boshlanish:.3f}s")
print(f"\n @cached_property (bir marta):")
boshlanish = time.perf_counter()
for _ in range(5):
_ = h.sozlar
print(f" 5 marta: {time.perf_counter() - boshlanish:.3f}s")
print(f"\n Kesh __dict__ ga yoziladi:")
print(f" 'sozlar' in h.__dict__ = {'sozlar' in h.__dict__}")
print(f" h.statistika = {h.statistika}")
print(f" __dict__ kalitlari: {sorted(h.__dict__)}")
print(f"\n ⚠️ TUZOQ — asos o'zgarsa kesh eskiradi:")
h.matn = "Butunlay boshqa matn"
print(f" h.matn o'zgardi, lekin:")
print(f" h.sozlar[:3] = {h.sozlar[:3]} ⚠️ eski qiymat!")
del h.__dict__["sozlar"]
del h.__dict__["statistika"]
print(f"\n del h.__dict__['sozlar'] dan keyin:")
print(f" h.sozlar = {h.sozlar} ✅")
print("\n=== 4. ✅ Keshni to'g'ri boshqarish ===\n")
class HujjatYaxshi:
"""Asos o'zgarganda kesh avtomatik tozalanadi."""
def __init__(self, matn: str):
self._matn = matn
self._kesh: dict[str, object] = {}
@property
def matn(self) -> str:
return self._matn
@matn.setter
def matn(self, qiymat: str) -> None:
self._matn = qiymat
self._kesh.clear() # ⭐ kesh bekor qilinadi
print(f" [kesh tozalandi]")
@property
def sozlar(self) -> list[str]:
if "sozlar" not in self._kesh:
print(f" [hisoblanmoqda: sozlar]")
self._kesh["sozlar"] = self._matn.split()
return self._kesh["sozlar"]
@property
def noyob(self) -> set[str]:
if "noyob" not in self._kesh:
print(f" [hisoblanmoqda: noyob]")
self._kesh["noyob"] = set(s.lower() for s in self.sozlar)
return self._kesh["noyob"]
hy = HujjatYaxshi("bir ikki uch bir ikki")
print(f" hy.sozlar →")
print(f" {hy.sozlar}")
print(f" hy.sozlar (2-marta) →")
print(f" {hy.sozlar} ← keshdan, hisoblash yo'q")
print(f" hy.noyob →")
print(f" {sorted(hy.noyob)}")
print(f"\n hy.matn = 'yangi matn' →")
hy.matn = "yangi matn keldi"
print(f" hy.sozlar →")
print(f" {hy.sozlar} ✅ qayta hisoblandi")
print("\n=== 5. Qachon nima ===\n")
print("""
┌────────────────────┬──────────────────────────────────────┐
│ Vosita │ Qachon │
├────────────────────┼──────────────────────────────────────┤
│ Oddiy atribut │ Mustaqil ma'lumot (a, b) │
│ @property │ Arzon hisoblash, asos o'zgaruvchan │
│ @cached_property │ Qimmat hisoblash, asos O'ZGARMAS │
│ Qo'lda kesh │ Qimmat + asos o'zgaradi │
│ Metod () │ Argument oladi yoki yon ta'siri bor │
└────────────────────┴──────────────────────────────────────┘
⭐ MEZON — property yoki metod?
Atribut kabi tuyulsa (ot): @property
t.yuza, h.sozlar, u.toliq_ism
Amal kabi tuyulsa (fe'l): metod
h.saqla(), t.aylantir(90)
Argument kerak bo'lsa: metod
h.qidir("naqsh")
Qimmat va kutilmagan bo'lsa: metod
db.hamma_qatorlar() ← property bo'lsa aldaydi
""")
print("=== 6. ⚠️ property tuzoqlari ===\n")
print(""" ⚠️ 1. Yon ta'sirli property:
@property
def malumot(self):
return tarmoqdan_yukla() # ⚠️ obj.malumot sekin!
✅ def malumot_yukla(self): ... metod qiling
⚠️ 2. Istisno tashlaydigan property:
@property
def x(self):
return self._d["x"] # ⚠️ KeyError kutilmagan
✅ return self._d.get("x")
⚠️ 3. Rekursiya:
@property
def x(self):
return self.x # ❌ cheksiz rekursiya
return self._x # ✅
⚠️ 4. Setter da o'zini chaqirish:
@x.setter
def x(self, q):
self.x = q # ❌ cheksiz rekursiya
self._x = q # ✅
""")Natijaning muhim qismi:
=== ⚠️ 1. Saqlangan hosila qiymat eskiradi ===
Boshida: a=3, b=4, yuza=12, perimetr=14
a=10: a=10, b=4, yuza=12 ⚠️, perimetr=14 ⚠️
Kutilgan: yuza=40, perimetr=28
=== 3. @cached_property — bir marta hisoblanadi ===
@property (har safar hisoblanadi):
5 marta: 0.103s
@cached_property (bir marta):
5 marta: 0.021s
⚠️ TUZOQ — asos o'zgarsa kesh eskiradi:
h.sozlar[:3] = ['Python', 'juda', 'kuchli'] ⚠️ eski qiymat!
=== 4. ✅ Keshni to'g'ri boshqarish ===
hy.sozlar →
[hisoblanmoqda: sozlar]
['bir', 'ikki', 'uch', 'bir', 'ikki']
hy.sozlar (2-marta) →
['bir', 'ikki', 'uch', 'bir', 'ikki'] ← keshdanNima ko'rsatdi: 2.5-bo'lim.
Misol 4 — Amaliy: konfiguratsiya sinfi
"""To'liq __init__ loyihasi — barcha tamoyillar birga."""
from __future__ import annotations
import json
import os
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Any, Literal
print("=== SERVER KONFIGURATSIYASI ===\n")
Muhit = Literal["ishlab_chiqish", "sinov", "ishlab_chiqarish"]
class Sozlama:
"""Server konfiguratsiyasi.
INVARIANTLAR:
1. 1 <= port <= 65535
2. muhit — uch qiymatdan biri
3. ishlab_chiqarish da debug = False
4. maks_ulanish >= 1
5. sirlar repr da ko'rinmaydi
"""
MUHITLAR: frozenset[str] = frozenset(
{"ishlab_chiqish", "sinov", "ishlab_chiqarish"}
)
SUKUT_PORT = 8000
PORT_ORALIGI = (1, 65535)
def __init__(
self,
host: str = "localhost",
port: int = SUKUT_PORT,
muhit: Muhit = "ishlab_chiqish",
*,
debug: bool | None = None,
maks_ulanish: int = 100,
ruxsat_etilgan: list[str] | None = None,
sirlar: dict[str, str] | None = None,
) -> None:
# ── 1. Tekshirish ──
if not isinstance(port, int) or isinstance(port, bool):
raise TypeError(f"Port butun son bo'lsin: {type(port).__name__}")
past, yuqori = self.PORT_ORALIGI
if not past <= port <= yuqori:
raise ValueError(f"Port {past}–{yuqori} oralig'ida: {port}")
if muhit not in self.MUHITLAR:
raise ValueError(
f"Muhit {sorted(self.MUHITLAR)} dan biri bo'lsin: {muhit!r}"
)
if maks_ulanish < 1:
raise ValueError(f"maks_ulanish >= 1 bo'lsin: {maks_ulanish}")
if not host.strip():
raise ValueError("Host bo'sh bo'lmasin")
# ── 2. Normallashtirish ──
self.host = host.strip().lower()
self.port = port
self.muhit: Muhit = muhit
self.maks_ulanish = maks_ulanish
# ── 3. Bog'liq qiymatlar (invariant 3) ──
if debug is None:
debug = muhit != "ishlab_chiqarish"
if debug and muhit == "ishlab_chiqarish":
raise ValueError("Ishlab chiqarishda debug yoqilmasin")
self.debug = debug
# ── 4. Nusxa olish ──
self.ruxsat_etilgan = list(ruxsat_etilgan or ["*"])
# ── 5. Ichki holat (hammasi shu yerda) ──
self._sirlar = dict(sirlar or {})
self._muzlatilgan = False
self._ozgartirishlar: list[str] = []
# ── Fabrika metodlari ──
@classmethod
def muhitdan(cls, prefiks: str = "APP_") -> Sozlama:
"""Muhit o'zgaruvchilaridan yaratadi."""
def olish(nom: str, sukut: Any = None) -> Any:
return os.environ.get(f"{prefiks}{nom.upper()}", sukut)
port = olish("PORT")
return cls(
host=olish("HOST", "localhost"),
port=int(port) if port else cls.SUKUT_PORT,
muhit=olish("ENV", "ishlab_chiqish"),
debug=olish("DEBUG", "").lower() in ("1", "true", "ha"),
maks_ulanish=int(olish("MAX_CONN", 100)),
)
@classmethod
def jsondan(cls, matn: str) -> Sozlama:
"""JSON satridan yaratadi."""
try:
m = json.loads(matn)
except json.JSONDecodeError as e:
raise ValueError(f"Noto'g'ri JSON: {e}") from e
if not isinstance(m, dict):
raise ValueError("JSON obyekt bo'lsin")
return cls(**m)
@classmethod
def sukut(cls, muhit: Muhit = "ishlab_chiqish") -> Sozlama:
"""Muhitga mos tayyor sozlama."""
tayyor = {
"ishlab_chiqish": dict(host="localhost", port=8000,
maks_ulanish=10),
"sinov": dict(host="0.0.0.0", port=8001,
maks_ulanish=50),
"ishlab_chiqarish": dict(host="0.0.0.0", port=80,
maks_ulanish=1000, debug=False),
}
return cls(muhit=muhit, **tayyor[muhit])
# ── Hosila qiymatlar ──
@property
def url(self) -> str:
sxema = "https" if self.muhit == "ishlab_chiqarish" else "http"
port = "" if self.port in (80, 443) else f":{self.port}"
return f"{sxema}://{self.host}{port}"
@property
def ishlab_chiqarishmi(self) -> bool:
return self.muhit == "ishlab_chiqarish"
@property
def muzlatilgan(self) -> bool:
return self._muzlatilgan
# ── Amallar ──
def sir(self, nom: str) -> str:
"""Sirni oladi (jurnal yozilmaydi)."""
if nom not in self._sirlar:
raise KeyError(f"Sir topilmadi: {nom!r}")
return self._sirlar[nom]
def sir_orn(self, nom: str, qiymat: str) -> None:
if self._muzlatilgan:
raise RuntimeError("Sozlama muzlatilgan")
self._sirlar[nom] = qiymat
self._ozgartirishlar.append(f"sir:{nom}")
def muzlat(self) -> None:
"""Boshqa o'zgartirib bo'lmaydi."""
self._muzlatilgan = True
def __setattr__(self, nom: str, qiymat: Any) -> None:
"""⭐ Muzlatilgandan keyin yozishni to'sadi."""
if getattr(self, "_muzlatilgan", False) and not nom.startswith("_"):
raise RuntimeError(f"Sozlama muzlatilgan: {nom}")
super().__setattr__(nom, qiymat)
def lugat(self, sirlar_bilan: bool = False) -> dict[str, Any]:
m = {
"host": self.host, "port": self.port, "muhit": self.muhit,
"debug": self.debug, "maks_ulanish": self.maks_ulanish,
"ruxsat_etilgan": list(self.ruxsat_etilgan),
}
if sirlar_bilan:
m["sirlar"] = dict(self._sirlar)
return m
def __repr__(self) -> str:
"""⭐ Sirlar KO'RINMAYDI."""
muz = " MUZLATILGAN" if self._muzlatilgan else ""
return (f"Sozlama({self.host!r}, port={self.port}, "
f"muhit={self.muhit!r}, debug={self.debug}, "
f"sirlar={len(self._sirlar)} ta{muz})")
print("1. Uch xil yaratish usuli:\n")
s1 = Sozlama()
s2 = Sozlama.sukut("ishlab_chiqarish")
s3 = Sozlama.jsondan('{"host": "api.wisar.uz", "port": 3000, '
'"muhit": "sinov"}')
os.environ.update({"APP_HOST": "test.local", "APP_PORT": "9000"})
s4 = Sozlama.muhitdan()
for nom, s in [("Sozlama()", s1), ("sukut('ishlab_chiqarish')", s2),
("jsondan(...)", s3), ("muhitdan()", s4)]:
print(f" {nom:<26} {s}")
print(f" {'':<26} url = {s.url}")
print("\n\n2. Invariant himoyasi:\n")
XATOLAR = [
("port=0", dict(port=0)),
("port=70000", dict(port=70000)),
("port='8000'", dict(port="8000")),
("port=True", dict(port=True)),
("muhit='prod'", dict(muhit="prod")),
("maks_ulanish=0", dict(maks_ulanish=0)),
("host=''", dict(host="")),
("ishlab_chiqarish + debug", dict(muhit="ishlab_chiqarish", debug=True)),
]
for tavsif, kwargs in XATOLAR:
try:
Sozlama(**kwargs)
natija = "⚠️ O'TDI"
except (ValueError, TypeError) as e:
natija = f"✅ {type(e).__name__}: {str(e)[:46]}"
print(f" {tavsif:<28} {natija}")
print("\n\n3. ⭐ Sirlar himoyasi:\n")
s = Sozlama(muhit="sinov")
s.sir_orn("db_parol", "juda-maxfiy-parol-123")
s.sir_orn("api_kalit", "sk-abc123xyz")
print(f" repr(s): {s!r}")
print(f" ✅ sirlar QIYMATI ko'rinmaydi\n")
print(f" s.lugat(): {s.lugat()}")
print(f" ✅ sirlar yo'q\n")
print(f" s.sir('db_parol') = {s.sir('db_parol')!r} ← aniq so'ralganda")
try:
s.sir("yoq")
except KeyError as e:
print(f" s.sir('yoq') → ✅ KeyError: {e}")
print("\n\n4. Muzlatish:\n")
print(f" Muzlatishdan oldin:")
s.port = 9999
print(f" s.port = 9999 ✅")
s.muzlat()
print(f"\n s.muzlat() dan keyin:")
for kod, f in [
("s.port = 8888", lambda: setattr(s, "port", 8888)),
("s.host = 'x'", lambda: setattr(s, "host", "x")),
("s.sir_orn('a', 'b')", lambda: s.sir_orn("a", "b")),
("s.yangi_atribut = 1", lambda: setattr(s, "yangi_atribut", 1)),
]:
try:
f(); n = "⚠️ O'TDI"
except RuntimeError as e:
n = f"✅ RuntimeError: {e}"
print(f" {kod:<24} {n}")
print(f"\n Yakuniy: {s!r}")
print("\n\n5. Hosila qiymatlar:\n")
for muhit, port in [("ishlab_chiqish", 8000), ("sinov", 8080),
("ishlab_chiqarish", 443), ("ishlab_chiqarish", 80)]:
try:
k = Sozlama(host="wisar.uz", port=port, muhit=muhit)
print(f" {muhit:<20} port={port:<6} → {k.url}")
except ValueError as e:
print(f" {muhit:<20} port={port:<6} → ❌ {e}")
print("\n\n6. dataclass versiyasi (solishtirish):\n")
@dataclass
class SozlamaD:
"""Xuddi shu narsa — kamroq kod."""
host: str = "localhost"
port: int = 8000
muhit: str = "ishlab_chiqish"
debug: bool = True
maks_ulanish: int = 100
ruxsat_etilgan: list[str] = field(default_factory=lambda: ["*"])
_sirlar: dict[str, str] = field(default_factory=dict, repr=False)
def __post_init__(self) -> None:
if not 1 <= self.port <= 65535:
raise ValueError(f"Port 1–65535: {self.port}")
if self.muhit not in Sozlama.MUHITLAR:
raise ValueError(f"Noma'lum muhit: {self.muhit!r}")
if self.debug and self.muhit == "ishlab_chiqarish":
raise ValueError("Ishlab chiqarishda debug yoqilmasin")
self.host = self.host.strip().lower()
@property
def url(self) -> str:
return f"http://{self.host}:{self.port}"
d = SozlamaD(host=" API.WISAR.UZ ", port=3000)
print(f" {d}")
print(f" url: {d.url}")
print(f" asdict: {asdict(d)}")
try:
SozlamaD(port=99999)
except ValueError as e:
print(f"\n Tekshiruv: ✅ ValueError: {e}")
print(f"""
┌────────────────────┬──────────────┬──────────────┐
│ Xususiyat │ Qo'lda │ dataclass │
├────────────────────┼──────────────┼──────────────┤
│ Kod qatorlari │ ~70 │ ~20 │
│ __init__ │ qo'lda │ avtomatik │
│ __repr__ │ qo'lda │ avtomatik │
│ __eq__ │ qo'lda │ avtomatik │
│ Sukut tuzog'i │ ehtiyot │ TAQIQLANGAN │
│ Tekshiruv │ __init__ │ __post_init__│
│ Nozik nazorat │ ✅ to'liq │ ⚠️ cheklangan│
│ __setattr__ nazorat│ ✅ │ ⚠️ frozen │
└────────────────────┴──────────────┴──────────────┘
⭐ TANLASH:
Asosan ma'lumot → dataclass
Murakkab tekshiruv va nazorat → qo'lda __init__
Ikkalasi ham → dataclass + __post_init__
""")Natijaning muhim qismi:
1. Uch xil yaratish usuli:
Sozlama() Sozlama('localhost', port=8000, muhit=...)
url = http://localhost:8000
sukut('ishlab_chiqarish') Sozlama('0.0.0.0', port=80, ...)
url = https://0.0.0.0
2. Invariant himoyasi:
port=0 ✅ ValueError: Port 1–65535 oralig'ida: 0
port='8000' ✅ TypeError: Port butun son bo'lsin: str
port=True ✅ TypeError: Port butun son bo'lsin: bool
ishlab_chiqarish + debug ✅ ValueError: Ishlab chiqarishda debug...
3. ⭐ Sirlar himoyasi:
repr(s): Sozlama('localhost', port=8000, muhit='sinov',
debug=True, sirlar=2 ta)
✅ sirlar QIYMATI ko'rinmaydi
4. Muzlatish:
s.port = 8888 ✅ RuntimeError: Sozlama muzlatilgan: port
s.sir_orn('a', 'b') ✅ RuntimeError: Sozlama muzlatilganNima ko'rsatdi: 2.1, 2.4, 2.5, 2.6, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"__init__ obyektni yaratadi" |
__new__ yaratadi, __init__ sozlaydi |
"def __init__(self, lst=[]) xavfsiz" |
Sukut bir marta yaratiladi |
"x or [] — x is None bilan bir xil" |
0, "", [] ni ham almashtiradi |
| "Atributni istalgan metodda yaratsa bo'ladi" | Texnik ha, lekin __init__ da e'lon qiling |
| "Hisoblangan qiymatni saqlash tez" | Eskiradi. @property yoki kesh boshqaruvi |
"__init__ bir nechta bo'lishi mumkin" |
Oxirgisi qoladi. @classmethod ishlating |
"self.x = x doim yetarli" |
Konteyner uchun nusxa oling |
"dataclass — faqat sodda holatlar" |
__post_init__ bilan murakkab ham |
6. Keng tarqalgan xatolar va yechimlari
1. O'zgaruvchan sukut
def __init__(self, lst=[]): # ❌
def __init__(self, lst=None): # ✅
self.lst = list(lst or [])2. or bilan falsy qiymatni yo'qotish
self.n = n or 100 # ⚠️ n=0 bo'lsa 100
self.n = 100 if n is None else n # ✅3. Nusxa olmaslik
self.lst = lst # ⚠️ tashqi havola
self.lst = list(lst) # ✅4. Atributni boshqa metodda yaratish
def hisobla(self):
self.natija = ... # ⚠️ __init__ da yo'q
def __init__(self):
self.natija = None # ✅ e'lon5. Hosila qiymatni saqlash
self.yuza = a * b # ⚠️ eskiradi
@property
def yuza(self): return self.a * self.b # ✅6. __init__ da og'ir ish
def __init__(self, url):
self.d = yukla(url) # ⚠️ sekin, xato beradi
@classmethod
def urldan(cls, url): # ✅
return cls(yukla(url))7. super().__init__() unutish
class B(A):
def __init__(self, a, b):
self.b = b # ⚠️ A.__init__ chaqirilmadi
super().__init__(a) # ✅8. dataclass da o'zgaruvchan sukut
lst: list = [] # ❌ ValueError
lst: list = field(default_factory=list) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 7.3-dars (o'tilgan): sukut argument tuzog'i — bu yerda takrorlandi
- 8.5-dars: sinf va nusxa atributlari farqi
- 8.7-dars:
super().__init__()va meros - 8.15-dars:
@propertyto'liq - 8.16-dars:
@classmethodfabrika metodlari - 8.17-dars:
dataclassva__post_init__to'liq - 13-qism:
pydantic— tekshiruv avtomatik
8. Eng yaxshi amaliyotlar
Sukut —
None, o'zgaruvchan obyekt emas. Har konteyner uchun.is Nonebilan tekshiring,orbilan emas.0,"",[]yo'qolmasin.Konteynerdan nusxa oling.
list(lst)— tashqi o'zgarishdan himoya.Barcha atributlarni
__init__da e'lon qiling. Boshqa metodda tug'ilmasin.Invariantni
__init__da tekshiring. Noto'g'ri obyekt yaratilmasin.Hosila qiymatni
@propertyqiling. Saqlangan qiymat eskiradi.__init__tez va ishonchli bo'lsin. Og'ir ish — fabrika metodida.Ko'p yaratish usuli kerak bo'lsa —
@classmethod.isinstanceshoxlanishi emas.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
1. class A:
def __init__(self, l=[]): self.l = l
a, b = A(), A()
a.l.append(1)
print(b.l)
2. class A:
def __init__(self, l=None): self.l = l or []
a, b = A(), A()
a.l.append(1)
print(b.l)
3. class A:
def __init__(self, n=None): self.n = n or 10
print(A(0).n)
4. class A:
def __init__(self, l): self.l = l
x = [1]
a = A(x)
x.append(2)
print(a.l)
5. class A:
def __init__(self, a, b):
self.a, self.b = a, b
self.s = a + b
x = A(1, 2)
x.a = 10
print(x.s)
6. class A:
def __init__(self): print("A")
def __init__(self): print("B")
A()
7. class A:
def __init__(self): return None
print(type(A()))
8. from dataclasses import dataclass
@dataclass
class A:
x: int = 0
print(A() == A())
9. class A:
def __init__(self, x): self.x = x
@property
def y(self): return self.x * 2
a = A(3)
a.x = 5
print(a.y)
10. class A:
def __init__(self, x): self.x = x
print(A(1).__dict__)
11. class A:
def __init__(self, **kw):
self.__dict__.update(kw)
print(A(a=1, b=2).__dict__)
12. class A:
def __init__(self, t=()): self.t = t
a, b = A(), A()
print(a.t is b.t)Javoblar
[1]— umumiy sukut ro'yxat[]— har safar yangi10—0 or 10→10[1, 2]— nusxa olinmadi3—seskirdiB— ikkinchi__init__birinchisini almashtiradi<class '__main__.A'>—return NoneruxsatTrue—dataclass__eq__beradi10—propertyhar safar hisoblaydi{'x': 1}{'a': 1, 'b': 2}True—tupleo'zgarmas, xavfsiz
Vazifa 2: Xatolarni tuzating
1. def __init__(self, d={}): self.d = d
2. def __init__(self, n=None): self.n = n or 100
3. def __init__(self, lst): self.lst = lst
4. def __init__(self, a, b):
self.a, self.b = a, b
self.yuza = a * b
5. def __init__(self, url): self.d = requests.get(url).json()
6. class B(A):
def __init__(self, x, y):
self.y = y
7. @dataclass
class A:
teglar: set = set()
8. def __init__(self, x):
self.x = x
def hisobla(self):
self.natija = self.x * 2Javoblar
1. def __init__(self, d=None): self.d = dict(d or {})
2. self.n = 100 if n is None else n
3. self.lst = list(lst)
4. @property
def yuza(self): return self.a * self.b
5. @classmethod
def urldan(cls, url): return cls(requests.get(url).json())
6. super().__init__(x) qo'shing
7. teglar: set = field(default_factory=set)
8. __init__ da: self.natija: int | None = NoneVazifa 3: Vaqt sinfi
Yozing:
__init__(self, soat, daqiqa, soniya=0)— to'liq tekshiruv- Invariant:
0<=soat<24,0<=daqiqa<60,0<=soniya<60 @property jami_soniya@classmethod soniyadan(cls, n)@classmethod satrdan(cls, "14:30:00")@classmethod hozir(cls)__repr__,__str__
Vazifa 4: Sukut tuzoq detektori
Vosita yozing:
- Modul sinflarini skanerlasin
__init__da o'zgaruvchan sukut argumentlarni topsin- O'zgaruvchan sinf atributlarini topsin
x or []naqshini topsin (falsy tuzog'i)- Nusxa olinmagan konteyner argumentlarini
- Har biri uchun tuzatish taklifi
Vazifa 5: Matritsa sinfi
__init__(self, qatorlar)— to'g'ri o'lchamni tekshirsin- Ichki ro'yxatlardan chuqur nusxa olsin
@property olcham,transponirlangan@classmethod nol(cls, n, m),birlik(cls, n)- Ichki ma'lumot tashqaridan o'zgarmasin
- Har amal yangi obyekt qaytarsin (o'zgarmas)
Vazifa 6: Kesh boshqaruvi
Hujjatsinfi —matnxususiyatisozlar,jumlalar,statistika— keshlanadiganmatno'zgarganda kesh avtomatik tozalansin@cached_propertyva qo'lda keshni solishtiring- Kesh urish/o'tkazib yuborish statistikasi
- Xotira hajmini o'lchang
Vazifa 7: O'ylash
Nega Python __init__ da self.x = x yozishni majburiy qiladi — nega konstruktor argumentlari avtomatik atributga aylanmaydi (TypeScript ning constructor(private x: number) kabi)?
Javob
Chunki avtomatik bog'lash aniqlikni yo'qotadi, va Pythonning javobi boshqa — dataclass.
1. Avtomatik bog'lash nima demak
Ba'zi tillarda konstruktor argumenti to'g'ridan-to'g'ri maydonga aylanadi:
// TypeScript
class Nuqta {
constructor(private x: number, public y: number) {}
// x va y avtomatik this.x, this.y bo'ladi
}// Kotlin
class Nuqta(val x: Double, val y: Double)// Scala
class Nuqta(val x: Double, val y: Double)Pythonda esa:
class Nuqta:
def __init__(self, x, y):
self.x = x # majburiy
self.y = y2. Nega Python bunday qilmagan
A) __init__ — oddiy metod, maxsus sintaksis yo'q
class A:
def __init__(self, x): ...__init__ — bu shunchaki maxsus nomli metod. Uning imzosi boshqa metodlardan farq qilmaydi. Avtomatik bog'lash uchun Python __init__ ni maxsus tarzda parse qilishi kerak bo'lardi — bu Pythonning "hamma narsa bir xil qoidaga bo'ysunadi" tamoyilini buzadi.
B) Argument ≠ atribut
Ko'p holatda ular mos kelmaydi:
class Doira:
def __init__(self, diametr): # argument
self.radius = diametr / 2 # atribut — boshqa nom, boshqa qiymat
class Foydalanuvchi:
def __init__(self, ism, familiya):
self.toliq_ism = f"{ism} {familiya}" # ikki argument — bir atribut
class Hisob:
def __init__(self, boshlangich_balans):
self._balans = boshlangich_balans # nom o'zgardi (_ prefiks)
self._tarix = [] # argumentsiz atribut
class Ulanish:
def __init__(self, url, foydalanuvchi, parol):
self.url = url
self._soket = ulan(url, foydalanuvchi, parol) # argumentlar
# ishlatildi, lekin saqlanmadiAgar bog'lash avtomatik bo'lsa, bu holatlar uchun istisno sintaksisi kerak bo'lardi.
C) Tekshiruv va normallashtirish qayerda?
def __init__(self, ism, yosh):
if yosh < 0:
raise ValueError(...)
self.ism = ism.strip().title() # normallashtirish
self.yosh = yoshAvtomatik bog'lash bilan bu bog'lashdan keyin bo'lishi kerak — ya'ni obyekt bir lahza noto'g'ri holatda bo'ladi. TypeScript da aynan shunday muammo bor.
D) "Explicit is better than implicit"
self.x = x yozilgan qatorni ko'rgan o'quvchi aniq biladi: bu atribut bor, ommaviy, argumentdan keladi. Avtomatik bo'lsa — imzoga qarab taxmin qilish kerak.
3. Pythonning javobi — dataclass
Python 3.7 (2018) da muammo boshqacha hal qilindi:
from dataclasses import dataclass
@dataclass
class Nuqta:
x: float
y: float = 0.0Bu — kod generatsiyasi, sintaksis o'zgarishi emas:
# @dataclass avtomatik yozadi:
def __init__(self, x: float, y: float = 0.0) -> None:
self.x = x
self.y = y
def __repr__(self): ...
def __eq__(self, other): ...Nega bu yaxshiroq:
TypeScript private x |
Python @dataclass |
|
|---|---|---|
| Sintaksis o'zgarishi | Til darajasida | Oddiy dekorator |
Faqat __init__ |
__repr__, __eq__ ham |
|
| Tekshiruv | Qo'shimcha kod | __post_init__ |
| Sukut tuzog'i | Mumkin | Taqiqlangan |
| O'chirib qo'yish | Oddiy __init__ yozing |
|
| Kutubxonalar | attrs, pydantic, msgspec |
4. dataclass nima beradi
@dataclass
class Foydalanuvchi:
ism: str
email: str
yosh: int = 0
teglar: list[str] = field(default_factory=list) # ⭐ tuzoq yo'q
parol: str = field(repr=False, default="") # ⭐ sirlar yashirin
def __post_init__(self):
if self.yosh < 0:
raise ValueError("Yosh manfiy bo'lmasin")
self.email = self.email.lower()Bepul:
u = Foydalanuvchi("Aziz", "A@B.uz", 30)
u # Foydalanuvchi(ism='Aziz', ...)
u == Foydalanuvchi("Aziz", "a@b.uz", 30) # True
asdict(u) # lug'atga
replace(u, yosh=31) # yangi nusxaQo'shimcha rejimlar:
@dataclass(frozen=True) # o'zgarmas + hashable
@dataclass(slots=True) # __slots__ (3.10+), xotira tejaydi
@dataclass(order=True) # <, >, <=, >=
@dataclass(kw_only=True) # faqat nomli argumentlar (3.10+)5. Ekotizim
dataclass — standart, lekin muqobillar ham bor:
| Kutubxona | Xususiyat |
|---|---|
dataclasses |
Standart, bog'liqliksiz |
attrs |
dataclass ning ilhomlantiruvchisi, ko'proq imkoniyat |
pydantic |
Ish vaqtida tekshiruv, JSON, FastAPI |
msgspec |
Eng tez serializatsiya |
NamedTuple |
Yengil, o'zgarmas, tuple kabi |
# pydantic — tur avtomatik tekshiriladi va o'giriladi
from pydantic import BaseModel, EmailStr, Field
class Foydalanuvchi(BaseModel):
ism: str = Field(min_length=1, max_length=100)
email: EmailStr
yosh: int = Field(ge=0, le=150)
Foydalanuvchi(ism="Aziz", email="a@b.uz", yosh="30") # "30" → 30
Foydalanuvchi(ism="", email="xato", yosh=-1) # ValidationError6. Qachon qo'lda __init__
dataclass hamma narsani hal qilmaydi:
# ⚠️ dataclass mos kelmaydi:
class Ulanish:
def __init__(self, url, foydalanuvchi, parol):
self._soket = ulan(url, foydalanuvchi, parol) # argument saqlanmaydi
self._urinish = 0
self.url = url
class Doira:
def __init__(self, diametr):
self.radius = diametr / 2 # nom o'zgaradi
class Kesh:
def __init__(self, hajm=100):
self._m = OrderedDict()
self._hajm = hajm
self._urish = self._otkazish = 0 # hosila holat Mezon: argumentlar atributlarga bir-bir mos kelsa — dataclass. Aks holda — qo'lda.
7. Tarixiy izoh
Guido dataclass dan oldin ham taklif qabul qilmagan edi. PEP 557 (2017) muallifi Eric V. Smith yozadi:
"Bu —
attrsdan ilhomlangan, lekin soddaroq. Maqsad —namedtupleva to'liq sinf orasidagi bo'shliqni to'ldirish."
Ya'ni yechim tilni o'zgartirmasdan, kutubxona darajasida topildi. Bu — Pythonning odatiy yo'li:
| Muammo | Yechim | Til o'zgardimi |
|---|---|---|
Ko'p self.x = x |
@dataclass |
dekorator |
| Xotira (ko'p nusxa) | __slots__ |
sinf atributi |
| Tur tekshiruvi | mypy |
tashqi vosita |
| Async | async/await |
sintaksis (kamdan-kam) |
Xulosa:
self.x = x majburiy, chunki:
__init__— oddiy metod, maxsus qoidasiz- Argument va atribut ko'pincha mos kelmaydi
- Tekshiruv va normallashtirish uchun joy kerak
- Aniqlik — Pythonning asosiy qadriyati
Lekin takrorlanishdan qutulish uchun @dataclass bor — u ko'proq narsa beradi (__repr__, __eq__, sukut tuzog'i himoyasi) va tilni o'zgartirmaydi.
Nimani mustahkamlaydi: 2.1, 2.2, 2.7-bo'limlar.
Xulosa
Bu darsda __init__ va atributlarni to'g'ri loyihalashni o'rgandik.
Eng muhim uch fikr:
Sukut qiymat tuzog'i sinflarda ikki xil ko'rinishda.
def __init__(self, lst=[])vaclass A: lst = []— ikkalasi ham barcha nusxalar uchun bitta obyekt yaratadi. Yechim:Nonesukut +__init__ichida yaratish, va konteynerdan nusxa olish (list(lst)).@dataclassbu tuzoqni umuman taqiqlaydi.__init__— invariantning birinchi qo'riqchisi. Noto'g'ri obyekt umuman yaratilmasin: tekshiring, normallashtiring, keyin o'rnating. Barcha nusxa atributlari shu yerda e'lon qilinsin — boshqa metodda tug'ilmasin.Hisoblanadigan qiymatni saqlamang.
self.yuza = a * b—ao'zgarsa eskiradi.@propertyhar safar hisoblaydi va doim to'g'ri; qimmat hisoblash uchun@cached_propertyyoki qo'lda boshqariladigan kesh, lekin unda asos o'zgarganda keshni bekor qilish shart.
Keyingi darsda metodlar va self ni chuqur ko'ramiz: bog'langan metodlar, self aslida qanday uzatiladi va metodlarni sinfdan tashqarida yozish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!