Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Sintaksis va asosiy tushunchalar
- 2.2. "is-a" munosabati
- 2.3. Liskov almashtirish tamoyili (LSP)
- 2.4. Metodni kengaytirish va almashtirish
- 2.5. isinstance va issubclass
- 2.6. Nima meros olinadi
- 2.7. Meros ierarxiyasini loyihalash
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Meros asoslari
- Misol 2 — "is-a" va Liskov tamoyili
- Misol 3 — Mo'rt asosiy sinf
- Misol 4 — Amaliy: hujjat ierarxiyasi
- 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.6-dars: Meros asoslari
8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 6-dars
1. Kirish va motivatsiya
Uch sinf, uch marta takrorlangan kod:
class Jangchi:
def __init__(self, nom, jon):
self.nom, self.jon = nom, jon
def zarar(self, n): self.jon = max(0, self.jon - n)
def tirikmi(self): return self.jon > 0
def hujum(self): return 15
class Kamonchi:
def __init__(self, nom, jon):
self.nom, self.jon = nom, jon # ⚠️ takror
def zarar(self, n): self.jon = max(0, self.jon - n) # ⚠️ takror
def tirikmi(self): return self.jon > 0 # ⚠️ takror
def hujum(self): return 12 # ← faqat shu farq qiladiMeros bilan:
class Jonzot:
def __init__(self, nom, jon):
self.nom, self.jon = nom, jon
def zarar(self, n): self.jon = max(0, self.jon - n)
def tirikmi(self): return self.jon > 0
def hujum(self): raise NotImplementedError
class Jangchi(Jonzot):
def hujum(self): return 15
class Kamonchi(Jonzot):
def hujum(self): return 12Lekin meros — eng ko'p noto'g'ri ishlatiladigan OOP vositasi:
class Kvadrat(Togriturtburchak): # ⚠️ mashhur muammo
...
class Foydalanuvchi(dict): # ⚠️ ehtiyot
...
class A(B, C, D, E): # ⚠️ MRO chalkashligi
...Bu darsda:
- Meros sintaksisi va atributlarni qayta aniqlash
- "is-a" munosabati — merosning yagona to'g'ri sababi
isinstancevaissubclass- Metodni kengaytirish va butunlay almashtirish
- Liskov almashtirish tamoyili
- Qachon meros, qachon kompozitsiya
2. Nazariya — chuqur tushuntirish
2.1. Sintaksis va asosiy tushunchalar
class Ota:
def f(self): return "ota-f"
def g(self): return "ota-g"
class Bola(Ota): # Ota dan meros
def g(self): return "bola-g" # qayta aniqlash (override)
def h(self): return "bola-h" # yangi metod
b = Bola()
b.f() # 'ota-f' ← Ota dan
b.g() # 'bola-g' ← qayta aniqlangan
b.h() # 'bola-h' ← yangiAtamalar:
| O'zbekcha | Inglizcha | Ma'nosi |
|---|---|---|
| Ota-sinf, asos sinf | base / parent / superclass | Meros beruvchi |
| Avlod sinf | derived / child / subclass | Meros oluvchi |
| Qayta aniqlash | override | Metodni almashtirish |
| Kengaytirish | extend | Yangi qo'shish |
Har sinf object dan meros oladi:
class A: pass
class A(object): pass # ≡ bir xil
A.__bases__ # (<class 'object'>,)
A.__mro__ # (A, object)2.2. "is-a" munosabati
Merosning yagona to'g'ri sababi:
Bola— bu bir turdagiOta.
# ✅ To'g'ri
class Hayvon: ...
class It(Hayvon): ... # It — bu Hayvon ✅
class Istisno(Exception): ... # Istisno — bu Exception ✅
class Admin(Foydalanuvchi): ... # Admin — bu Foydalanuvchi ✅# ❌ Noto'g'ri
class Dvigatel: ...
class Mashina(Dvigatel): ... # Mashina — bu Dvigatel EMAS
# Mashinada dvigatel BOR (has-a)
class Stack(list): ... # ⚠️ Stack — list emas
# Stack ichida list BORTest: "X — bu Y" jumlasi mantiqan to'g'rimi?
| Jumla | To'g'ri? | Yechim |
|---|---|---|
| "It — bu Hayvon" | Meros | |
| "Mashinada dvigatel bor" | has-a | Kompozitsiya |
| "Talaba — bu Odam" | Meros | |
| "Buyurtmada mahsulotlar bor" | has-a | Kompozitsiya |
| "Kvadrat — bu To'rtburchak" | Keyingi bo'limga qarang |
2.3. Liskov almashtirish tamoyili (LSP)
Barbara Liskov (1987):
Agar
S—Tning avlodi bo'lsa,Tturidagi obyektlarniSbilan almashtirish dasturning to'g'riligini buzmasligi kerak.
Oddiy tilda: avlod sinf ota-sinf o'rniga ishlashi kerak.
Klassik buzilish — Kvadrat/To'rtburchak:
class Togriturtburchak:
def __init__(self, en, boy):
self.en, self.boy = en, boy
def yuza(self): return self.en * self.boy
class Kvadrat(Togriturtburchak): # ⚠️ matematik "is-a"
def __init__(self, tomon):
super().__init__(tomon, tomon)
@Togriturtburchak.en.setter # tomonlar bog'liq bo'lishi kerak
def en(self, q):
self._en = self._boy = q
def test(t: Togriturtburchak):
t.en = 5
t.boy = 4
assert t.yuza() == 20 # ⚠️ Kvadrat uchun 16! Muammo: matematikada kvadrat — to'rtburchak, lekin o'zgaruvchan obyektlarda emas. Togriturtburchak ning shartnomasi: "en va boy mustaqil".
Yechimlar:
# 1. O'zgarmas qilish — muammo yo'qoladi
@dataclass(frozen=True)
class Togriturtburchak:
en: float
boy: float
@dataclass(frozen=True)
class Kvadrat(Togriturtburchak):
def __init__(self, tomon):
super().__init__(tomon, tomon) # ✅ o'zgarmaydi
# 2. Umumiy ota-sinf
class Shakl(ABC):
@abstractmethod
def yuza(self): ...
class Togriturtburchak(Shakl): ...
class Kvadrat(Shakl): ... # ✅ birodar, avlod emasLSP buzilishining belgilari:
# ⚠️ 1. Avlodda NotImplementedError
class Bola(Ota):
def metod(self):
raise NotImplementedError("Bola bu metodni qo'llamaydi")
# ⚠️ 2. Avlod shartni qattiqlashtiradi
class Ota:
def f(self, x: int): ...
class Bola(Ota):
def f(self, x: int):
if x < 0: raise ValueError(...) # ⚠️ Ota qabul qilardi
# ⚠️ 3. isinstance tekshiruvlari paydo bo'ladi
if isinstance(x, Kvadrat): ... # ⚠️ polimorfizm buzilgan
# ⚠️ 4. Avlod ota-sinf invariantini buzadi2.4. Metodni kengaytirish va almashtirish
A) Butunlay almashtirish:
class Ota:
def f(self): return "ota"
class Bola(Ota):
def f(self): return "bola" # ota-sinf mantiqi ishlatilmaydiB) Kengaytirish (super() bilan):
class Bola(Ota):
def f(self):
natija = super().f() # ⭐ ota-sinf mantiqi
return f"{natija} + bola"Uch naqsh:
class Bola(Ota):
def oldin(self):
self.tayyorla() # oldin
return super().oldin()
def keyin(self):
n = super().keyin()
self.tozala() # keyin
return n
def orasida(self):
self.boshla()
n = super().orasida()
self.tugat()
return nsuper() — 8.7-darsda batafsil.
2.5. isinstance va issubclass
class A: pass
class B(A): pass
b = B()
isinstance(b, B) # True
isinstance(b, A) # True ✅ meros hisobga olinadi
isinstance(b, object) # True
type(b) is A # False ⚠️ aniq tur
issubclass(B, A) # True
issubclass(A, B) # False
issubclass(B, B) # True ⚠️ o'ziga ham True
issubclass(B, object) # TrueBir nechta tur:
isinstance(x, (int, float)) # ✅ ikkalasidan biri
isinstance(x, int | float) # ✅ 3.10+ isinstance — oxirgi chora:
# ⚠️ Tur bo'yicha shoxlanish — polimorfizm buzilgan
def yuza(shakl):
if isinstance(shakl, Doira):
return 3.14 * shakl.r ** 2
elif isinstance(shakl, Kvadrat):
return shakl.a ** 2
# ✅ Polimorfizm
def yuza(shakl):
return shakl.yuza()Qachon isinstance to'g'ri:
# ✅ 1. Turli turlarni qabul qilish
def f(x: int | str):
if isinstance(x, str):
x = int(x)
# ✅ 2. Chegarada tekshirish
def __eq__(self, boshqa):
if not isinstance(boshqa, Nuqta):
return NotImplemented
# ✅ 3. Rekursiv tuzilmalar
def tekis(x):
if isinstance(x, (list, tuple)):
return [b for a in x for b in tekis(a)]
return [x]2.6. Nima meros olinadi
class Ota:
SINF_ATR = "ota"
def __init__(self):
self.nusxa_atr = "ota"
def metod(self): ...
@property
def xususiyat(self): return 1
@classmethod
def sinf_metod(cls): ...
@staticmethod
def statik(): ...
class Bola(Ota):
pass| Nima | Meros olinadimi |
|---|---|
| Sinf atributlari | |
| Metodlar | |
@property |
|
@classmethod, @staticmethod |
|
| Dunder metodlar | |
__init__ |
(agar avlodda yo'q bo'lsa) |
| Nusxa atributlari | __init__ chaqirilsa |
__slots__ |
maxsus qoidalar (8.19) |
Xususiy (__nom) |
nom buzilishi (8.10) |
__init__ avlodda aniqlansa — ota-sinfniki chaqirilmaydi:
class Bola(Ota):
def __init__(self):
self.oziniki = 1 # ⚠️ nusxa_atr yo'q!
Bola().nusxa_atr # AttributeError super().__init__() chaqiring.
2.7. Meros ierarxiyasini loyihalash
Qoidalar:
Chuqurlik ≤ 2-3 daraja. Chuqurroq — kompozitsiyani ko'rib chiqing.
Ota-sinf — umumiy shartnoma. Avlodlar uni buzmasin.
Faqat kod qayta ishlatish uchun meros olmang. Bu — kompozitsiya ishi.
Ota-sinf o'zgarishi avlodlarni buzmasin (mo'rt asosiy sinf).
Mo'rt asosiy sinf muammosi:
class Royxat:
def __init__(self): self._el = []
def qosh(self, x):
self._el.append(x)
def kop_qosh(self, xs):
for x in xs:
self.qosh(x) # ⚠️ o'z metodini chaqiradi
class SanovchiRoyxat(Royxat):
def __init__(self):
super().__init__()
self.soni = 0
def qosh(self, x):
self.soni += 1
super().qosh(x)
def kop_qosh(self, xs):
self.soni += len(xs) # ⚠️
super().kop_qosh(xs) # → qosh() ni chaqiradi → yana sanaydi
s = SanovchiRoyxat()
s.kop_qosh([1, 2, 3])
s.soni # 6 ⚠️ 3 kutilgan edi Sabab: avlod ota-sinfning ichki chaqiruv tartibiga bog'liq bo'lib qoldi. Ota-sinf kop_qosh ni o'zgartirsa — avlod buziladi.
Yechimlar:
- Ota-sinf ichki chaqiruvlarni hujjatlashi (
__bilan xususiy metodlar) - Yoki kompozitsiya ishlatish
3. Tez ma'lumotnoma
Sintaksis
class Bola(Ota):
def metod(self): qayta aniqlash
n = super().metod() ota-sinf mantiqi
return n + " qo'shimcha"
Bola.__bases__ (<class 'Ota'>,)
Bola.__mro__ (Bola, Ota, object)
Bola.__base__ <class 'Ota'>Merosning yagona sababi
"Bola — bu bir turdagi Ota" (is-a)
✅ It — Hayvon
❌ Mashina — Dvigatel → kompozitsiya (has-a)
❌ Stack — list → kompozitsiyaLSP
Avlod ota-sinf O'RNIGA ishlashi kerak.
Buzilish belgilari:
• Avlodda NotImplementedError
• Avlod shartni qattiqlashtiradi
• isinstance tekshiruvlari paydo bo'ladi
• Avlod invariantni buzadiTekshirish
isinstance(b, A) ✅ meros bilan
issubclass(B, A) sinflar uchun
type(b) is A ⚠️ aniq tur
issubclass(B, B) True — o'ziga hamLoyihalash
Chuqurlik ≤ 2-3 daraja
Ota-sinf shartnomasini buzmang
Kod qayta ishlatish ≠ meros sababi
Ota-sinf ichki chaqiruvlarini hujjatlang4. Batafsil misollar
Misol 1 — Meros asoslari
"""Merosning barcha asosiy jihatlari."""
print("=== 1. Nima meros olinadi ===\n")
class Ota:
SINF_ATR = "ota-sinf-atributi"
_ICHKI = "ota-ichki"
def __init__(self, x):
self.nusxa_atr = x
def metod(self):
return "ota.metod"
@property
def xususiyat(self):
return "ota.xususiyat"
@classmethod
def sinf_metod(cls):
return f"sinf_metod, cls={cls.__name__}"
@staticmethod
def statik():
return "statik"
def __repr__(self):
return f"{type(self).__name__}({self.nusxa_atr!r})"
class Bola(Ota):
pass # hech narsa qo'shmadi
b = Bola("qiymat")
print(f" Bola hech narsa qo'shmadi, lekin:")
TEKSHIRUV = [
("b.SINF_ATR", lambda: b.SINF_ATR),
("b._ICHKI", lambda: b._ICHKI),
("b.nusxa_atr", lambda: b.nusxa_atr),
("b.metod()", lambda: b.metod()),
("b.xususiyat", lambda: b.xususiyat),
("b.sinf_metod()", lambda: b.sinf_metod()),
("b.statik()", lambda: b.statik()),
("repr(b)", lambda: repr(b)),
]
for kod, f in TEKSHIRUV:
print(f" {kod:<20} → {f()!r}")
print(f"\n Bola.__dict__ da o'zining nechta atributi bor: "
f"{len([n for n in Bola.__dict__ if not n.startswith('__')])}")
print(f" Lekin dir(b) da: {len([n for n in dir(b) if not n.startswith('__')])} ta")
print("\n=== 2. Qayta aniqlash va kengaytirish ===\n")
class Asos:
def almashtiriladi(self):
return "asos"
def kengaytiriladi(self):
return "asos"
def oldin_keyin(self):
return "asos-mantiq"
class Avlod(Asos):
def almashtiriladi(self):
return "avlod" # ota-sinf ISHLATILMAYDI
def kengaytiriladi(self):
return super().kengaytiriladi() + " + avlod" # ⭐ ishlatiladi
def oldin_keyin(self):
return f"[oldin] {super().oldin_keyin()} [keyin]"
a, av = Asos(), Avlod()
print(f" {'Metod':<20} {'Asos':<20} {'Avlod'}")
print(" " + "─" * 62)
for m in ["almashtiriladi", "kengaytiriladi", "oldin_keyin"]:
print(f" {m:<20} {getattr(a, m)()!r:<20} {getattr(av, m)()!r}")
print("\n=== ⚠️ 3. __init__ va meros ===\n")
class OtaInit:
def __init__(self, a):
self.a = a
self.tayyorlangan = True
class BolaYomon(OtaInit):
def __init__(self, a, b):
self.b = b # ⚠️ super() yo'q
class BolaYaxshi(OtaInit):
def __init__(self, a, b):
super().__init__(a) # ✅
self.b = b
for Sinf in (BolaYomon, BolaYaxshi):
obj = Sinf(1, 2)
bor = sorted(obj.__dict__)
try:
_ = obj.a
natija = f"✅ obj.a = {obj.a}"
except AttributeError as e:
natija = f"❌ AttributeError: {e}"
print(f" {Sinf.__name__:<14} __dict__={bor}")
print(f" {'':<14} {natija}")
print(f"""
⭐ Avlodda __init__ aniqlansa, ota-sinfniki AVTOMATIK
chaqirilmaydi. super().__init__(...) yozish SHART.
⚠️ Bu — merosdagi eng ko'p uchraydigan xato.
""")
print("=== 4. isinstance va issubclass ===\n")
class Hayvon: pass
class Sutemizuvchi(Hayvon): pass
class It(Sutemizuvchi): pass
class Mushuk(Sutemizuvchi): pass
class Qush(Hayvon): pass
it = It()
print(f" MRO: {' → '.join(c.__name__ for c in It.__mro__)}\n")
print(f" {'Ifoda':<34} {'Natija'}")
print(" " + "─" * 46)
IFODALAR = [
("isinstance(it, It)", isinstance(it, It)),
("isinstance(it, Sutemizuvchi)", isinstance(it, Sutemizuvchi)),
("isinstance(it, Hayvon)", isinstance(it, Hayvon)),
("isinstance(it, object)", isinstance(it, object)),
("isinstance(it, Mushuk)", isinstance(it, Mushuk)),
("type(it) is It", type(it) is It),
("type(it) is Hayvon", type(it) is Hayvon),
("issubclass(It, Hayvon)", issubclass(It, Hayvon)),
("issubclass(Hayvon, It)", issubclass(Hayvon, It)),
("issubclass(It, It)", issubclass(It, It)),
("isinstance(it, (Qush, It))", isinstance(it, (Qush, It))),
]
for kod, natija in IFODALAR:
belgi = "✅" if natija else " "
print(f" {kod:<34} {natija!s:<8} {belgi}")
print(f"""
⚠️ issubclass(It, It) = True — sinf O'ZINING avlodi hisoblanadi.
Bu matematik kelishuv (refleksivlik).
""")
print("=== ⚠️ 5. isinstance bilan shoxlanish — antipattern ===\n")
import math
class Shakl: pass
class Doira(Shakl):
def __init__(self, r): self.r = r
class Kvadrat(Shakl):
def __init__(self, a): self.a = a
class Uchburchak(Shakl):
def __init__(self, a, h): self.a, self.h = a, h
def yuza_yomon(s):
"""⚠️ Har yangi shakl uchun BU FUNKSIYANI o'zgartirish kerak."""
if isinstance(s, Doira):
return math.pi * s.r ** 2
elif isinstance(s, Kvadrat):
return s.a ** 2
elif isinstance(s, Uchburchak):
return s.a * s.h / 2
raise TypeError(f"Noma'lum shakl: {type(s).__name__}")
class ShaklYaxshi:
def yuza(self): raise NotImplementedError
class DoiraY(ShaklYaxshi):
def __init__(self, r): self.r = r
def yuza(self): return math.pi * self.r ** 2
class KvadratY(ShaklYaxshi):
def __init__(self, a): self.a = a
def yuza(self): return self.a ** 2
class UchburchakY(ShaklYaxshi):
def __init__(self, a, h): self.a, self.h = a, h
def yuza(self): return self.a * self.h / 2
print(f" ⚠️ isinstance bilan:")
for s in [Doira(3), Kvadrat(4), Uchburchak(6, 5)]:
print(f" {type(s).__name__:<12} {yuza_yomon(s):>8.2f}")
print(f"\n ✅ Polimorfizm bilan:")
for s in [DoiraY(3), KvadratY(4), UchburchakY(6, 5)]:
print(f" {type(s).__name__:<12} {s.yuza():>8.2f}")
print(f"""
⭐ YANGI SHAKL QO'SHISH:
⚠️ isinstance: yuza_yomon() ni O'ZGARTIRISH kerak
(va boshqa 10 ta shunday funksiyani ham)
✅ Polimorfizm: faqat yangi sinf yozasiz
Mavjud kod O'ZGARMAYDI
Bu — "Ochiq/yopiq tamoyili" (Open/Closed Principle):
Kengaytirish uchun OCHIQ, o'zgartirish uchun YOPIQ.
""")
print("=== 6. Meros zanjiri ===\n")
class A:
def kim(self): return "A"
def faqat_a(self): return "faqat A da"
class B(A):
def kim(self): return "B"
class C(B):
pass
class D(C):
def kim(self): return "D"
print(f" Ierarxiya: A → B → C → D\n")
print(f" {'Sinf':<8} {'MRO':<32} {'kim()':<8} {'kim manbai'}")
print(" " + "─" * 66)
for S in (A, B, C, D):
mro = " → ".join(c.__name__ for c in S.__mro__)
manba = next(c.__name__ for c in S.__mro__ if "kim" in c.__dict__)
print(f" {S.__name__:<8} {mro:<32} {S().kim():<8} {manba}")
print(f"\n faqat_a() barcha sinflarda:")
for S in (A, B, C, D):
print(f" {S.__name__}().faqat_a() = {S().faqat_a()!r}")
print(f"""
⭐ Qidiruv MRO bo'yicha, birinchi topilgan g'olib.
C da kim() yo'q → B dan olinadi.
""")Natijaning muhim qismi:
=== 1. Nima meros olinadi ===
Bola hech narsa qo'shmadi, lekin:
b.SINF_ATR → 'ota-sinf-atributi'
b.metod() → 'ota.metod'
b.sinf_metod() → 'sinf_metod, cls=Bola'
repr(b) → "Bola('qiymat')"
Bola.__dict__ da o'zining nechta atributi bor: 0
Lekin dir(b) da: 7 ta
=== ⚠️ 3. __init__ va meros ===
BolaYomon __dict__=['b']
❌ AttributeError: 'BolaYomon' object has no attribute 'a'
BolaYaxshi __dict__=['a', 'b', 'tayyorlangan']
✅ obj.a = 1
=== 6. Meros zanjiri ===
Sinf MRO kim() kim manbai
──────────────────────────────────────────────────────────────────
A A → object A A
B B → A → object B B
C C → B → A → object B B
D D → C → B → A → object D DNima ko'rsatdi: 2.1, 2.4, 2.5, 2.6-bo'limlar.
Misol 2 — "is-a" va Liskov tamoyili
"""Merosni qachon ishlatish — va qachon ishlatmaslik."""
from dataclasses import dataclass
from abc import ABC, abstractmethod
print("=== ⚠️ 1. Klassik muammo: Kvadrat/Togriturtburchak ===\n")
class Togriturtburchak:
"""SHARTNOMA: en va boy MUSTAQIL o'zgaradi."""
def __init__(self, en: float, boy: float):
self._en, self._boy = en, boy
@property
def en(self): return self._en
@en.setter
def en(self, q): self._en = q
@property
def boy(self): return self._boy
@boy.setter
def boy(self, q): self._boy = q
def yuza(self): return self._en * self._boy
def __repr__(self):
return f"{type(self).__name__}(en={self._en}, boy={self._boy})"
class Kvadrat(Togriturtburchak):
"""⚠️ SHARTNOMANI BUZADI: tomonlar bog'liq."""
def __init__(self, tomon: float):
super().__init__(tomon, tomon)
@Togriturtburchak.en.setter
def en(self, q):
self._en = self._boy = q # ⚠️ boy ham o'zgardi
@Togriturtburchak.boy.setter
def boy(self, q):
self._en = self._boy = q # ⚠️
def mijoz_kodi(t: Togriturtburchak) -> tuple[float, bool]:
"""Togriturtburchak SHARTNOMASIGA tayanadi."""
t.en = 5
t.boy = 4
return t.yuza(), t.yuza() == 20
print(f" Mijoz kodi: t.en=5; t.boy=4; kutiladi yuza==20\n")
for obj in [Togriturtburchak(1, 1), Kvadrat(1)]:
yuza, togri = mijoz_kodi(obj)
belgi = "✅" if togri else "❌ LSP BUZILDI"
print(f" {type(obj).__name__:<18} yuza={yuza:<6} {belgi}")
print(f"""
⭐ MUAMMO:
Matematikada kvadrat — to'rtburchak.
Lekin O'ZGARUVCHAN obyektlarda "is-a" — bu XATTI-HARAKAT
haqida, shakl haqida emas.
Togriturtburchak shartnomasi: "en va boy mustaqil".
Kvadrat buni buzadi → LSP buzilishi.
""")
print("=== ✅ 2. Yechim A: o'zgarmas qilish ===\n")
@dataclass(frozen=True)
class TT:
en: float
boy: float
@property
def yuza(self): return self.en * self.boy
def olcham(self, en=None, boy=None) -> "TT":
"""Yangi obyekt qaytaradi."""
return TT(en if en is not None else self.en,
boy if boy is not None else self.boy)
@dataclass(frozen=True)
class Kv(TT):
def __init__(self, tomon: float):
super().__init__(tomon, tomon)
def olcham(self, en=None, boy=None) -> TT:
"""⭐ TT qaytaradi — Kv emas (kvadrat bo'lmasligi mumkin)."""
return TT(en if en is not None else self.en,
boy if boy is not None else self.boy)
t1 = TT(5, 4)
k1 = Kv(3)
print(f" t1 = {t1}, yuza = {t1.yuza}")
print(f" k1 = {k1}, yuza = {k1.yuza}")
t2 = t1.olcham(en=10)
k2 = k1.olcham(en=10)
print(f"\n t1.olcham(en=10) = {t2}, yuza = {t2.yuza}")
print(f" k1.olcham(en=10) = {k2}, yuza = {k2.yuza} ✅ TT qaytdi")
print(f"\n Asl obyektlar o'zgarmadi: t1={t1}, k1={k1} ✅")
print(f"""
⭐ O'ZGARMASLIK LSP muammosini YO'Q QILADI:
• Setter yo'q → shartnoma buzilmaydi
• Har o'zgarish — yangi obyekt
• Kvadrat o'lchami o'zgarsa — TT bo'ladi (to'g'ri!)
""")
print("=== ✅ 3. Yechim B: birodar sinflar ===\n")
class Shakl(ABC):
"""Umumiy shartnoma — faqat yuza va perimetr."""
@abstractmethod
def yuza(self) -> float: ...
@abstractmethod
def perimetr(self) -> float: ...
def tavsif(self) -> str:
return (f"{type(self).__name__}: yuza={self.yuza():.2f}, "
f"perimetr={self.perimetr():.2f}")
@dataclass
class TogriturtburchakS(Shakl):
en: float
boy: float
def yuza(self): return self.en * self.boy
def perimetr(self): return 2 * (self.en + self.boy)
@dataclass
class KvadratS(Shakl):
tomon: float
def yuza(self): return self.tomon ** 2
def perimetr(self): return 4 * self.tomon
@dataclass
class DoiraS(Shakl):
r: float
def yuza(self): return 3.14159 * self.r ** 2
def perimetr(self): return 2 * 3.14159 * self.r
SHAKLLAR = [TogriturtburchakS(5, 4), KvadratS(3), DoiraS(2)]
for s in SHAKLLAR:
print(f" {s.tavsif()}")
print(f"\n Jami yuza: {sum(s.yuza() for s in SHAKLLAR):.2f}")
print(f" MRO: KvadratS → {' → '.join(c.__name__ for c in KvadratS.__mro__[1:])}")
print(f"""
⭐ KvadratS va TogriturtburchakS — BIRODAR, avlod emas.
Ular umumiy SHARTNOMANI (Shakl) bajaradi,
lekin bir-birining o'rniga ishlashi SHART EMAS.
""")
print("=== ⚠️ 4. Boshqa LSP buzilishlari ===\n")
print(""" A) Avlod xatolik tashlaydi:
class Qush:
def uch(self): return "uchdi"
class Pingvin(Qush):
def uch(self):
raise NotImplementedError("Pingvin ucholmaydi") ⚠️
def sayohat(q: Qush):
q.uch() # ⚠️ Pingvin bilan buziladi
✅ YECHIM:
class Qush: ...
class UchuvchiQush(Qush):
def uch(self): ...
class Pingvin(Qush): ... # uch() umuman yo'q
B) Avlod shartni QATTIQLASHTIRADI:
class Ombor:
def saqla(self, kalit: str, qiymat): ...
class ChegaralanganOmbor(Ombor):
def saqla(self, kalit: str, qiymat):
if len(kalit) > 10: ⚠️ Ota qabul qilardi
raise ValueError(...)
⭐ QOIDA: avlod KO'PROQ qabul qilishi, KAMROQ emas.
(kirish — bo'shashadi, chiqish — qattiqlashadi)
C) Avlod BOSHQA turni qaytaradi:
class Ota:
def olish(self) -> list: return []
class Bola(Ota):
def olish(self) -> dict: return {} ⚠️
⭐ QOIDA: qaytarish turi TORAYISHI mumkin:
list → list ✅
object → str ✅ (aniqroq)
list → dict ❌ (boshqa)
D) Avlod invariantni buzadi:
class Hisob:
# INVARIANT: balans >= 0
def yech(self, n):
if n > self._balans: raise ValueError(...)
class KreditHisob(Hisob):
def yech(self, n):
self._balans -= n ⚠️ manfiy bo'lishi mumkin
✅ Umumiy ota-sinf yarating yoki invariantni
boshidan kengroq qiling.
""")
print("=== 5. LSP tekshiruvchi test ===\n")
def lsp_test(Ota, Bola, sinovlar):
"""Bola Ota o'rniga ishlaydimi — tekshiradi."""
natijalar = []
for nom, tayyorla, amal, kutilgan in sinovlar:
qatorlar = []
for S in (Ota, Bola):
try:
obj = tayyorla(S)
natija = amal(obj)
qatorlar.append(("✅" if natija == kutilgan(obj)
else f"❌ {natija}"))
except Exception as e:
qatorlar.append(f"❌ {type(e).__name__}")
natijalar.append((nom, *qatorlar))
return natijalar
SINOVLAR = [
("en/boy mustaqil",
lambda S: S(1) if S is Kvadrat else S(1, 1),
lambda o: (setattr(o, "en", 5), setattr(o, "boy", 4), o.yuza())[-1],
lambda o: 20),
("yuza musbat",
lambda S: S(3) if S is Kvadrat else S(3, 3),
lambda o: o.yuza() > 0,
lambda o: True),
("repr ishlaydi",
lambda S: S(2) if S is Kvadrat else S(2, 2),
lambda o: isinstance(repr(o), str),
lambda o: True),
]
print(f" {'Sinov':<22} {'Togriturtburchak':<20} {'Kvadrat'}")
print(" " + "─" * 56)
for nom, r1, r2 in lsp_test(Togriturtburchak, Kvadrat, SINOVLAR):
print(f" {nom:<22} {r1:<20} {r2}")
print(f"""
⭐ AMALIY QOIDA:
Ota-sinf uchun yozilgan HAR TEST avlod bilan ham o'tishi kerak.
Agar o'tmasa — bu meros EMAS.
Testlarni parametrlashtiring:
@pytest.mark.parametrize("Sinf", [Ota, Bola1, Bola2])
def test_shartnoma(Sinf): ...
""")Natijaning muhim qismi:
=== ⚠️ 1. Klassik muammo: Kvadrat/Togriturtburchak ===
Mijoz kodi: t.en=5; t.boy=4; kutiladi yuza==20
Togriturtburchak yuza=20 ✅
Kvadrat yuza=16 ❌ LSP BUZILDI
=== ✅ 2. Yechim A: o'zgarmas qilish ===
t1.olcham(en=10) = TT(en=10, boy=4), yuza = 40
k1.olcham(en=10) = TT(en=10, boy=3), yuza = 30 ✅ TT qaytdi
Asl obyektlar o'zgarmadi: t1=TT(en=5, boy=4), k1=Kv(en=3, boy=3) ✅
=== 5. LSP tekshiruvchi test ===
Sinov Togriturtburchak Kvadrat
────────────────────────────────────────────────────────
en/boy mustaqil ✅ ❌ 16
yuza musbat ✅ ✅
repr ishlaydi ✅ ✅Nima ko'rsatdi: 2.2, 2.3-bo'limlar.
Misol 3 — Mo'rt asosiy sinf
"""Ota-sinf o'zgarishi avlodlarni qanday buzadi."""
print("=== ⚠️ 1. Klassik mo'rt asosiy sinf ===\n")
class RoyxatV1:
"""1-versiya."""
def __init__(self):
self._el = []
def qosh(self, x):
self._el.append(x)
def kop_qosh(self, xs):
for x in xs:
self.qosh(x) # ⚠️ o'z metodini chaqiradi
def __len__(self):
return len(self._el)
class Sanovchi(RoyxatV1):
"""Qo'shilganlarni sanaydi."""
def __init__(self):
super().__init__()
self.soni = 0
def qosh(self, x):
self.soni += 1
super().qosh(x)
def kop_qosh(self, xs):
self.soni += len(xs) # ⚠️
super().kop_qosh(xs)
s = Sanovchi()
s.qosh("a")
print(f" s.qosh('a') → soni={s.soni}, len={len(s)} ✅")
s2 = Sanovchi()
s2.kop_qosh(["a", "b", "c"])
print(f" s2.kop_qosh(3 ta) → soni={s2.soni}, len={len(s2)} "
f"⚠️ soni 3 bo'lishi kerak edi")
print(f"""
⭐ SABAB:
kop_qosh() → super().kop_qosh() → tsiklda self.qosh()
→ Sanovchi.qosh() → soni yana oshadi
Ya'ni har element IKKI MARTA sanaladi.
""")
print("=== ⚠️ 2. Ota-sinf o'zgarsa ===\n")
class RoyxatV2:
"""2-versiya: kop_qosh optimallashtirildi."""
def __init__(self):
self._el = []
def qosh(self, x):
self._el.append(x)
def kop_qosh(self, xs):
self._el.extend(xs) # ⭐ endi qosh() chaqirilmaydi
def __len__(self):
return len(self._el)
class SanovchiV2(RoyxatV2):
"""AYNAN o'sha kod."""
def __init__(self):
super().__init__()
self.soni = 0
def qosh(self, x):
self.soni += 1
super().qosh(x)
def kop_qosh(self, xs):
self.soni += len(xs)
super().kop_qosh(xs)
s3 = SanovchiV2()
s3.kop_qosh(["a", "b", "c"])
print(f" V2 bilan: soni={s3.soni}, len={len(s3)} ✅ endi to'g'ri")
print(f"""
⚠️ MUAMMO: Sanovchi kodi O'ZGARMADI, lekin xatti-harakati
ota-sinfning ICHKI AMALGA OSHIRILISHIGA qarab o'zgardi.
V1: soni = 6 ❌
V2: soni = 3 ✅
Bu — MO'RT ASOSIY SINF (fragile base class) muammosi.
""")
print("=== ✅ 3. Yechim A: ichki chaqiruvlarni yashirish ===\n")
class RoyxatXavfsiz:
"""Ichki amalga oshirish XUSUSIY metodlar orqali."""
def __init__(self):
self._el = []
def qosh(self, x):
self.__qosh(x) # ⭐ xususiy
def kop_qosh(self, xs):
for x in xs:
self.__qosh(x) # ⭐ avlod ushlab qololmaydi
def __qosh(self, x):
"""Xususiy — nom buzilishi tufayli qayta aniqlab bo'lmaydi."""
self._el.append(x)
def __len__(self):
return len(self._el)
class SanovchiXavfsiz(RoyxatXavfsiz):
def __init__(self):
super().__init__()
self.soni = 0
def qosh(self, x):
self.soni += 1
super().qosh(x)
def kop_qosh(self, xs):
self.soni += len(xs)
super().kop_qosh(xs)
s4 = SanovchiXavfsiz()
s4.qosh("a")
s4.kop_qosh(["b", "c", "d"])
print(f" qosh(1) + kop_qosh(3) → soni={s4.soni}, len={len(s4)} ✅")
print(f"\n Xususiy metod nomi: "
f"{[n for n in RoyxatXavfsiz.__dict__ if 'qosh' in n]}")
print(f"""
⭐ __qosh → _RoyxatXavfsiz__qosh (nom buzilishi, 8.10-dars)
Avlod uni tasodifan qayta aniqlay olmaydi.
⚠️ Lekin bu ATAYLAB kengaytirishni ham to'sadi.
""")
print("=== ✅ 4. Yechim B: kompozitsiya ===\n")
class SanovchiKompozitsiya:
"""Merosdan voz kechildi — ichida ro'yxat BOR."""
def __init__(self):
self._royxat = RoyxatV1() # ⭐ ichida
self.soni = 0
def qosh(self, x):
self.soni += 1
self._royxat.qosh(x)
def kop_qosh(self, xs):
self.soni += len(xs)
for x in xs:
self._royxat.qosh(x) # ⭐ ANIQ nima chaqirilishini bilamiz
def __len__(self):
return len(self._royxat)
s5 = SanovchiKompozitsiya()
s5.qosh("a")
s5.kop_qosh(["b", "c", "d"])
print(f" qosh(1) + kop_qosh(3) → soni={s5.soni}, len={len(s5)} ✅")
print(f"""
⭐ KOMPOZITSIYA AFZALLIKLARI:
✅ Ota-sinf ichki o'zgarishlariga BOG'LIQ EMAS
✅ Faqat OMMAVIY API ishlatiladi
✅ Ro'yxatni boshqa amalga oshirish bilan almashtirish oson
✅ Nima chaqirilishi ANIQ ko'rinadi
⚠️ Kamchiligi:
• isinstance(s5, RoyxatV1) → False
• Har metodni qo'lda uzatish kerak
""")
print("=== ✅ 5. Yechim C: shablon metod naqshi ===\n")
class RoyxatShablon:
"""Kengaytirish NUQTALARI aniq belgilangan."""
def __init__(self):
self._el = []
# ── Ommaviy API (qayta aniqlanmasin) ──
def qosh(self, x):
self._qoshishdan_oldin(x) # ⭐ ilgak
self._el.append(x)
self._qoshilgandan_keyin(x) # ⭐ ilgak
def kop_qosh(self, xs):
xs = list(xs)
self._kop_qoshishdan_oldin(xs) # ⭐ ilgak
for x in xs:
self._qoshishdan_oldin(x)
self._el.append(x)
self._qoshilgandan_keyin(x)
# ── Kengaytirish nuqtalari (avlodlar uchun) ──
def _qoshishdan_oldin(self, x): pass
def _qoshilgandan_keyin(self, x): pass
def _kop_qoshishdan_oldin(self, xs): pass
def __len__(self):
return len(self._el)
class SanovchiShablon(RoyxatShablon):
def __init__(self):
super().__init__()
self.soni = 0
self.jurnal = []
def _qoshilgandan_keyin(self, x): # ⭐ faqat ilgak
self.soni += 1
self.jurnal.append(f"+{x}")
class TekshiruvchiShablon(RoyxatShablon):
def _qoshishdan_oldin(self, x):
if not isinstance(x, str):
raise TypeError(f"Faqat satr: {type(x).__name__}")
s6 = SanovchiShablon()
s6.qosh("a")
s6.kop_qosh(["b", "c", "d"])
print(f" SanovchiShablon:")
print(f" soni={s6.soni}, len={len(s6)} ✅")
print(f" jurnal={s6.jurnal}")
t6 = TekshiruvchiShablon()
t6.qosh("ok")
try:
t6.qosh(42)
except TypeError as e:
print(f"\n TekshiruvchiShablon:")
print(f" t6.qosh(42) → ✅ TypeError: {e}")
print(f"""
⭐ SHABLON METOD NAQSHI:
• Ota-sinf ALGORITMNI boshqaradi
• Avlodlar faqat ILGAKLARNI (hook) to'ldiradi
• Ichki chaqiruv tartibi ota-sinf ichida qoladi
Bu — merosning ENG XAVFSIZ shakli.
""")
print("=== 6. Solishtirish ===\n")
print("""
┌────────────────────┬──────────┬──────────────┬────────────┐
│ Yondashuv │ Mo'rtlik │ Moslashuvchi │ Kod hajmi │
├────────────────────┼──────────┼──────────────┼────────────┤
│ Oddiy meros │ ⚠️ yuqori│ ✅ yuqori │ ✅ kam │
│ Xususiy metodlar │ ✅ past │ ⚠️ past │ ✅ kam │
│ Kompozitsiya │ ✅ yo'q │ ✅ yuqori │ ⚠️ ko'p │
│ Shablon metod │ ✅ past │ ✅ o'rta │ ⚠️ o'rta │
└────────────────────┴──────────┴──────────────┴────────────┘
⭐ TANLASH:
Kutubxona yozyapsizmi (boshqalar meros oladi)?
→ Shablon metod + ilgaklar, ichki chaqiruvlarni hujjatlang
Ichki kodmi (siz nazorat qilasiz)?
→ Oddiy meros yetadi
Uchinchi tomon sinfini kengaytiryapsizmi?
→ Kompozitsiya (ular ichini istalgan vaqt o'zgartiradi)
""")Natijaning muhim qismi:
=== ⚠️ 1. Klassik mo'rt asosiy sinf ===
s.qosh('a') → soni=1, len=1 ✅
s2.kop_qosh(3 ta) → soni=6, len=3 ⚠️ soni 3 bo'lishi kerak edi
=== ⚠️ 2. Ota-sinf o'zgarsa ===
V2 bilan: soni=3, len=3 ✅ endi to'g'ri
⚠️ MUAMMO: Sanovchi kodi O'ZGARMADI, lekin xatti-harakati
ota-sinfning ICHKI AMALGA OSHIRILISHIGA qarab o'zgardi.
=== ✅ 5. Yechim C: shablon metod naqshi ===
SanovchiShablon:
soni=4, len=4 ✅
jurnal=['+a', '+b', '+c', '+d']
TekshiruvchiShablon:
t6.qosh(42) → ✅ TypeError: Faqat satr: intNima ko'rsatdi: 2.7-bo'lim.
Misol 4 — Amaliy: hujjat ierarxiyasi
"""To'g'ri loyihalangan meros ierarxiyasi."""
from __future__ import annotations
import html
import json
import re
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import date
print("=== HUJJAT TIZIMI ===\n")
class Hujjat(ABC):
"""Barcha hujjatlar uchun umumiy shartnoma.
SHARTNOMA:
• matn — hujjat mazmuni (satr)
• render() — chiqish formatiga o'giradi
• KENGAYTMA, MIME — sinf darajasida
AVLODLAR: render() ni MAJBURIY amalga oshiradi.
Boshqa metodlar ixtiyoriy kengaytiriladi.
"""
KENGAYTMA: str = ".txt"
MIME: str = "text/plain"
MAKS_HAJM = 1_000_000
def __init__(self, nom: str, matn: str = "", muallif: str = ""):
if not nom.strip():
raise ValueError("Nom bo'sh bo'lmasin")
if len(matn) > self.MAKS_HAJM:
raise ValueError(f"Juda katta: {len(matn)} > {self.MAKS_HAJM}")
self.nom = nom.strip()
self.matn = matn
self.muallif = muallif
self.yaratilgan = date.today()
self._teglar: set[str] = set()
# ── Abstrakt (avlodlar amalga oshiradi) ──
@abstractmethod
def render(self) -> str:
"""Hujjatni chiqish formatiga o'giradi."""
# ── Umumiy (barcha avlodlar uchun) ──
@property
def fayl_nomi(self) -> str:
xavfsiz = re.sub(r"[^\w\s-]", "", self.nom).strip()
xavfsiz = re.sub(r"[\s]+", "-", xavfsiz).lower()
return f"{xavfsiz}{self.KENGAYTMA}"
@property
def teglar(self) -> tuple[str, ...]:
return tuple(sorted(self._teglar))
def teg_qosh(self, *teglar: str) -> None:
self._teglar.update(t.strip().lower() for t in teglar if t.strip())
def statistika(self) -> dict[str, int]:
return {
"belgilar": len(self.matn),
"sozlar": len(self.matn.split()),
"qatorlar": self.matn.count("\n") + 1 if self.matn else 0,
}
# ── Kengaytirish nuqtalari (ilgaklar) ──
def _sarlavha(self) -> str:
"""Avlodlar qayta aniqlashi mumkin."""
return self.nom
def _ostki_matn(self) -> str:
"""Avlodlar qayta aniqlashi mumkin."""
return f"Muallif: {self.muallif}" if self.muallif else ""
def __repr__(self) -> str:
return (f"{type(self).__name__}({self.nom!r}, "
f"{len(self.matn)} belgi)")
def __str__(self) -> str:
return f"{self.fayl_nomi} ({self.MIME})"
class MatnHujjat(Hujjat):
"""Oddiy matn."""
KENGAYTMA = ".txt"
MIME = "text/plain"
def render(self) -> str:
qismlar = [self._sarlavha(), "=" * len(self._sarlavha()), "",
self.matn]
ostki = self._ostki_matn()
if ostki:
qismlar += ["", "-" * 40, ostki]
return "\n".join(qismlar)
class MarkdownHujjat(Hujjat):
"""Markdown formati."""
KENGAYTMA = ".md"
MIME = "text/markdown"
def render(self) -> str:
qismlar = [f"# {self._sarlavha()}", ""]
if self._teglar:
qismlar.append(" ".join(f"`{t}`" for t in self.teglar))
qismlar.append("")
qismlar.append(self.matn)
ostki = self._ostki_matn()
if ostki:
qismlar += ["", "---", f"*{ostki}*"]
return "\n".join(qismlar)
def _ostki_matn(self) -> str:
"""⭐ Kengaytirish — sana ham qo'shildi."""
asos = super()._ostki_matn()
sana = f"Sana: {self.yaratilgan:%d.%m.%Y}"
return f"{asos} · {sana}" if asos else sana
class HtmlHujjat(Hujjat):
"""HTML formati."""
KENGAYTMA = ".html"
MIME = "text/html"
SHABLON = ("<!DOCTYPE html>\n<html lang=\"uz\">\n<head>\n"
" <meta charset=\"utf-8\">\n <title>{sarlavha}</title>\n"
"</head>\n<body>\n{tana}\n</body>\n</html>")
def render(self) -> str:
sarlavha = html.escape(self._sarlavha())
paragraflar = "\n".join(
f" <p>{html.escape(q)}</p>"
for q in self.matn.split("\n\n") if q.strip()
)
tana = f" <h1>{sarlavha}</h1>\n{paragraflar}"
ostki = self._ostki_matn()
if ostki:
tana += f"\n <footer>{html.escape(ostki)}</footer>"
return self.SHABLON.format(sarlavha=sarlavha, tana=tana)
class JsonHujjat(Hujjat):
"""Strukturalangan ma'lumot."""
KENGAYTMA = ".json"
MIME = "application/json"
def render(self) -> str:
return json.dumps({
"nom": self.nom,
"muallif": self.muallif,
"sana": self.yaratilgan.isoformat(),
"teglar": list(self.teglar),
"matn": self.matn,
"statistika": self.statistika(),
}, ensure_ascii=False, indent=2)
class MaqolaHujjat(MarkdownHujjat):
"""⭐ Ikkinchi daraja — Markdown ni kengaytiradi."""
KENGAYTMA = ".article.md"
def __init__(self, nom, matn="", muallif="", *, qisqacha: str = ""):
super().__init__(nom, matn, muallif)
self.qisqacha = qisqacha
def render(self) -> str:
"""⭐ Ota-sinf natijasiga qo'shimcha."""
asos = super().render()
if self.qisqacha:
qatorlar = asos.split("\n")
qatorlar.insert(2, f"> {self.qisqacha}\n")
return "\n".join(qatorlar)
return asos
print("1. Ierarxiya:\n")
TURLAR = [MatnHujjat, MarkdownHujjat, HtmlHujjat, JsonHujjat, MaqolaHujjat]
print(f" {'Sinf':<18} {'Kengaytma':<14} {'MIME':<22} {'Chuqurlik'}")
print(" " + "─" * 66)
for S in TURLAR:
chuqurlik = len(S.__mro__) - 2 # object va ABC ni hisobga olmaymiz
print(f" {S.__name__:<18} {S.KENGAYTMA:<14} {S.MIME:<22} {chuqurlik}")
print(f"\n MaqolaHujjat MRO:")
print(f" {' → '.join(c.__name__ for c in MaqolaHujjat.__mro__)}")
print("\n\n2. Bir mazmun — turli render:\n")
MATN = ("Python — kuchli dasturlash tili.\n\n"
"U o'rganish oson va imkoniyatlari keng.")
hujjatlar = []
for S in TURLAR:
kw = {"qisqacha": "Python haqida qisqacha"} if S is MaqolaHujjat else {}
h = S("Python haqida", MATN, "Aziz Karimov", **kw)
h.teg_qosh("python", "dasturlash", "Darslik")
hujjatlar.append(h)
for h in hujjatlar:
print(f" ┌─ {type(h).__name__} → {h.fayl_nomi}")
natija = h.render()
for q in natija.split("\n")[:6]:
print(f" │ {q[:64]}")
qolgan = len(natija.split("\n")) - 6
if qolgan > 0:
print(f" │ ... yana {qolgan} qator")
print(f" └{'─' * 60}\n")
print("3. ⭐ Polimorfizm:\n")
def hammasini_saqla(hujjatlar: list[Hujjat]) -> dict[str, int]:
"""⭐ Aniq turni BILMAYDI — faqat Hujjat shartnomasiga tayanadi."""
natija = {}
for h in hujjatlar:
natija[h.fayl_nomi] = len(h.render())
return natija
for nom, hajm in hammasini_saqla(hujjatlar).items():
print(f" {nom:<34} {hajm:>6} belgi")
print(f"\n Jami: {sum(hammasini_saqla(hujjatlar).values())} belgi")
print("\n\n4. Umumiy metodlar (bir marta yozilgan):\n")
h = hujjatlar[1]
print(f" {type(h).__name__} uchun:")
print(f" statistika() = {h.statistika()}")
print(f" teglar = {h.teglar}")
print(f" fayl_nomi = {h.fayl_nomi}")
print(f" str() = {h}")
print(f" repr() = {h!r}")
print(f"\n ⭐ Bularning hammasi Hujjat da BIR MARTA yozilgan.")
print("\n\n5. Kengaytirish nuqtasi (_ostki_matn):\n")
print(f" {'Sinf':<18} {'_ostki_matn()'}")
print(" " + "─" * 62)
for h in hujjatlar:
print(f" {type(h).__name__:<18} {h._ostki_matn()!r}")
print(f"""
⭐ MarkdownHujjat._ostki_matn():
super()._ostki_matn() + sana
Bu — KENGAYTIRISH, almashtirish emas.
""")
print("=== 6. Shartnoma tekshiruvi ===\n")
def shartnoma_testi(Sinf: type[Hujjat]) -> list[tuple[str, bool, str]]:
"""⭐ Har avlod uchun BIR XIL testlar (LSP)."""
natijalar = []
kw = {"qisqacha": "test"} if Sinf is MaqolaHujjat else {}
sinovlar = [
("render() satr qaytaradi",
lambda: isinstance(Sinf("T", "matn", **kw).render(), str)),
("render() bo'sh emas",
lambda: len(Sinf("T", "matn", **kw).render()) > 0),
("bo'sh matn ishlaydi",
lambda: isinstance(Sinf("T", "", **kw).render(), str)),
("bo'sh nom xato beradi",
lambda: _xato_beradi(lambda: Sinf("", "m", **kw), ValueError)),
("fayl_nomi kengaytma bilan",
lambda: Sinf("T", **kw).fayl_nomi.endswith(Sinf.KENGAYTMA)),
("teg qo'shish ishlaydi",
lambda: _teg_test(Sinf, kw)),
("statistika() to'g'ri",
lambda: Sinf("T", "bir ikki", **kw).statistika()["sozlar"] == 2),
]
for nom, f in sinovlar:
try:
ok = bool(f())
natijalar.append((nom, ok, "" if ok else "False qaytdi"))
except Exception as e:
natijalar.append((nom, False, f"{type(e).__name__}: {e}"))
return natijalar
def _xato_beradi(f, tur) -> bool:
try:
f()
return False
except tur:
return True
def _teg_test(Sinf, kw) -> bool:
h = Sinf("T", **kw)
h.teg_qosh("A", "b", " ")
return h.teglar == ("a", "b")
print(f" {'Sinov':<28} " + " ".join(f"{S.__name__[:7]:<8}" for S in TURLAR))
print(" " + "─" * 74)
barcha = {S.__name__: shartnoma_testi(S) for S in TURLAR}
sinov_nomlari = [n for n, _, _ in barcha[TURLAR[0].__name__]]
for i, nom in enumerate(sinov_nomlari):
qatorlar = []
for S in TURLAR:
_, ok, _ = barcha[S.__name__][i]
qatorlar.append(f"{'✅' if ok else '❌':<8}")
print(f" {nom:<28} " + " ".join(qatorlar))
jami = sum(1 for r in barcha.values() for _, ok, _ in r if ok)
kutilgan = len(TURLAR) * len(sinov_nomlari)
print(f"\n Natija: {jami}/{kutilgan} test o'tdi")
print(f"""
⭐ BU IERARXIYA NEGA TO'G'RI:
1. "is-a" haqiqiy: MarkdownHujjat — bu Hujjat ✅
2. Chuqurlik ≤ 3 daraja
3. Ota-sinf ABSTRAKT — o'zi ishlatilmaydi
4. Shartnoma aniq: render() majburiy
5. Kengaytirish nuqtalari belgilangan (_sarlavha, _ostki_matn)
6. Har avlod ota-sinf O'RNIGA ishlaydi (LSP ✅)
7. Umumiy kod bir marta yozilgan
8. Yangi format qo'shish — mavjud kodni o'zgartirmaydi
""")Natijaning muhim qismi:
1. Ierarxiya:
Sinf Kengaytma MIME Chuqurlik
──────────────────────────────────────────────────────────────────
MatnHujjat .txt text/plain 2
MarkdownHujjat .md text/markdown 2
MaqolaHujjat .article.md text/markdown 3
MaqolaHujjat MRO:
MaqolaHujjat → MarkdownHujjat → Hujjat → ABC → object
3. ⭐ Polimorfizm:
python-haqida.txt 166 belgi
python-haqida.md 171 belgi
python-haqida.html 288 belgi
=== 6. Shartnoma tekshiruvi ===
Sinov MatnHuj Markdow HtmlHuj JsonHuj Maqola
──────────────────────────────────────────────────────────────────────
render() satr qaytaradi ✅ ✅ ✅ ✅ ✅
bo'sh nom xato beradi ✅ ✅ ✅ ✅ ✅
statistika() to'g'ri ✅ ✅ ✅ ✅ ✅
Natija: 35/35 test o'tdiNima ko'rsatdi: 2.1, 2.2, 2.3, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Meros — kod qayta ishlatish uchun" | "is-a" uchun. Qayta ishlatish — kompozitsiya |
"Avlodda __init__ bo'lsa ota-sinfniki ham ishlaydi" |
super().__init__() chaqiring |
| "Kvadrat — to'rtburchak, meros to'g'ri" | O'zgaruvchan obyektlarda LSP buziladi |
| "Chuqur ierarxiya — yaxshi loyiha" | 2-3 darajadan chuqur — muammo belgisi |
"isinstance — polimorfizm" |
Aksincha, uni buzadi |
| "Ota-sinfni o'zgartirsam avlodlar ishlaydi" | Mo'rt asosiy sinf muammosi |
"type(x) == A va isinstance bir xil" |
isinstance merosni hisobga oladi |
| "Pythonda polimorfizm uchun meros kerak" | Duck typing yetadi |
6. Keng tarqalgan xatolar va yechimlari
1. super().__init__() unutish
class B(A):
def __init__(self, x, y):
self.y = y # ⚠️
super().__init__(x) # ✅2. "has-a" uchun meros
class Mashina(Dvigatel): ... # ❌
class Mashina:
def __init__(self):
self.dvigatel = Dvigatel() # ✅3. Avlodda NotImplementedError
class Pingvin(Qush):
def uch(self):
raise NotImplementedError # ⚠️ LSP buzilishi4. isinstance bilan shoxlanish
if isinstance(s, Doira): ... # ⚠️
return s.yuza() # ✅5. Ota-sinf o'zgaruvchan atributini kengaytirish
class B(A):
TEGLAR = A.TEGLAR
TEGLAR.add("yangi") # ⚠️ A niki ham o'zgardi
TEGLAR = A.TEGLAR | {"yangi"} # ✅6. Chuqur ierarxiya
A → B → C → D → E → F # ⚠️
A → B + kompozitsiya # ✅7. dict/list dan meros
class Sozlama(dict): ... # ⚠️ ko'p metod, kutilmagan xatti-harakat
class Sozlama(UserDict): ... # ✅ collections.UserDict
class Sozlama: # ✅ kompozitsiya
def __init__(self): self._d = {}8. Ota-sinf ichki chaqiruvlariga tayanish
def kop_qosh(self, xs):
self.soni += len(xs)
super().kop_qosh(xs) # ⚠️ ichida qosh() chaqirilishi mumkin7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8.7-dars:
super()— kengaytirish mexanizmi - 8.8-dars: ko'p meros va MRO
- 8.9-dars: polimorfizm — merossiz ham
- 8.18-dars: ABC — abstrakt asos sinflar
- 8.20-dars: kompozitsiya vs meros — to'liq solishtirish
- 9-qism: istisnolar ierarxiyasi — merosning eng yaxshi misoli
- Kitob: Design Patterns (GoF), Effective Python (Brett Slatkin)
8. Eng yaxshi amaliyotlar
"X — bu Y" testini o'tkazing. O'tmasa — kompozitsiya.
super().__init__()ni doim chaqiring. Ota-sinf holati kerak.Chuqurlik ≤ 2-3 daraja. Chuqurroq — qayta ko'rib chiqing.
Ota-sinf shartnomasini hujjatlang. Nima kafolatlanadi, nima kengaytiriladi.
Kengaytirish nuqtalarini aniq belgilang. Shablon metod naqshi.
Ota-sinf testlarini avlodlarda ham ishlating. LSP tekshiruvi.
isinstanceo'rniga polimorfizm. Tur bo'yicha shoxlanish — antipattern.O'rnatilgan turlardan meros olmang.
UserDict,UserListyoki kompozitsiya.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
1. class A:
def f(self): return "A"
class B(A): pass
print(B().f())
2. class A:
def __init__(self): self.x = 1
class B(A):
def __init__(self): self.y = 2
print(hasattr(B(), "x"))
3. class A: x = 1
class B(A): pass
B.x = 2
print(A.x, B.x)
4. class A: pass
class B(A): pass
print(issubclass(B, A), issubclass(A, B), issubclass(B, B))
5. class A:
def f(self): return "A"
class B(A):
def f(self): return super().f() + "B"
print(B().f())
6. class A: pass
print(A.__bases__, A.__mro__)
7. class A:
def f(self): return type(self).__name__
class B(A): pass
print(A().f(), B().f())
8. class A:
@classmethod
def yarat(cls): return cls()
class B(A): pass
print(type(B.yarat()).__name__)
9. class A: lst = []
class B(A): pass
B.lst.append(1)
print(A.lst)
10. class A:
def f(self): return self.g()
def g(self): return "A.g"
class B(A):
def g(self): return "B.g"
print(B().f())
11. class A: pass
a = A()
print(isinstance(a, object), issubclass(A, object))
12. class A:
def __init__(self, x): self.x = x
class B(A):
def __init__(self, x, y):
super().__init__(x)
self.y = y
print(sorted(B(1, 2).__dict__))Javoblar
A— meros olindiFalse—super().__init__()chaqirilmadi1 2—B.__dict__ga yozildiTrue False TrueAB— kengaytirish(<class 'object'>,) (<class '__main__.A'>, <class 'object'>)A B—type(self)haqiqiy sinfB—clshaqiqiy sinf[1]— bir ro'yxatB.g— mo'rt asosiy sinf mexanizmiTrue True['x', 'y']
Vazifa 2: Meros yoki kompozitsiya?
1. Mashina va Dvigatel
2. Talaba va Odam
3. Stack va list
4. AdminFoydalanuvchi va Foydalanuvchi
5. Buyurtma va Mahsulot
6. Kvadrat va Togriturtburchak (o'zgaruvchan)
7. ValidationError va Exception
8. Kesh va dict
9. Uy va Xona
10. JsonHujjat va HujjatJavoblar
- Kompozitsiya — mashinada dvigatel bor
- Meros — talaba bu odam
- Kompozitsiya — stack ichida list bor (
listning barcha metodlari kerak emas) - Meros — admin bu foydalanuvchi
- Kompozitsiya — buyurtmada mahsulotlar bor
- Ikkalasi ham emas — birodar sinflar (umumiy
Shaklota-sinfi) - Meros —
ValidationErrorbuException - Kompozitsiya yoki
UserDict— kesh dict emas (chegara, TTL bor) - Kompozitsiya — uyda xonalar bor
- Meros — JSON hujjat bu hujjat
Vazifa 3: Xodim ierarxiyasi
Yozing:
Xodim—ism,maosh,oylik(),tavsif()Menejer(Xodim)— jamoa uchun ustamaSotuvchi(Xodim)— sotuvdan foizStajyor(Xodim)— maoshning 50%- Barcha sinflar uchun bir xil test to'plami (LSP)
isinstanceishlatmasdan umumiy hisobot funksiyasi
Vazifa 4: LSP buzilishi detektori
Vosita yozing:
- Sinf ierarxiyasini skanerlasin
- Avlodda
NotImplementedErrortashlaydiganlarni topsin - Avlod qo'shimcha tekshiruv qo'shganini topsin
- Qaytarish turi mos kelmasligini
super().__init__()chaqirilmaganini- Har biri uchun tuzatish taklifi
Vazifa 5: Shablon metod naqshi
MalumotQuvuri abstrakt sinfi:
bajar()— algoritmni boshqaradi (qayta aniqlanmasin)- Ilgaklar:
_yukla(),_tekshir(),_ozgartir(),_saqla() - Ixtiyoriy ilgaklar:
_boshlashdan_oldin(),_tugagandan_keyin() - Uchta avlod: CSV, JSON, XML
- Xato bo'lsa
_xato_yuz_berdi()chaqirilsin - Har bosqich vaqtini o'lchang
Vazifa 6: Mo'rt asosiy sinf
- Ota-sinf yozing, unda
f()g()ni chaqirsin - Avlod
g()ni qayta aniqlasin — muammoni ko'rsating - Xususiy metod bilan tuzating
- Kompozitsiya bilan tuzating
- Shablon metod bilan tuzating
- Uchalasini solishtiring
Vazifa 7: O'ylash
Nega ko'p tajribali dasturchilar "kompozitsiyani merosdan afzal ko'ring" deydi — meros OOP ning to'rt ustunidan biri bo'lsa ham?
Javob
Chunki meros eng kuchli bog'lanishni yaratadi, va uning foydasi odatda kutilgandan kam.
1. Meros — eng kuchli bog'lanish
Bog'lanish darajalari (kuchsizdan kuchligacha):
# 1. Xabar (eng kuchsiz)
kanal.yubor("hodisa", malumot)
# 2. Funksiya chaqiruvi
natija = ishla(malumot)
# 3. Interfeys (Protocol/ABC)
def f(x: Ombor): x.saqla(...)
# 4. Kompozitsiya
class A:
def __init__(self): self._b = B()
# 5. MEROS (eng kuchli)
class A(B): ... Meros bilan A:
Bning barcha ommaviy metodlarini oladiBning barcha himoyalangan (_) a'zolarini ko'radiBning ichki chaqiruv tartibiga bog'liq bo'ladiBning invariantlariga rioya qilishi kerakBo'zgarsa — buzilishi mumkin
Bu — "banan so'radingiz, gorilla va o'rmon oldingiz" muammosi (Joe Armstrong).
2. Meros — kompilyatsiya vaqtida qotadi
# ⚠️ Meros — o'zgartirib bo'lmaydi
class Xizmat(HttpMijoz):
def sorov(self): ...
# Test uchun HttpMijoz ni almashtira olmaysiz
# ✅ Kompozitsiya — ish vaqtida almashtiriladi
class Xizmat:
def __init__(self, mijoz: Mijoz):
self._mijoz = mijoz
Xizmat(HttpMijoz()) # ishlab chiqarish
Xizmat(SoxtaMijoz()) # test
Xizmat(KeshMijoz(HttpMijoz())) # kesh qatlamiBu — bog'liqlikni kiritish (dependency injection). Testlash osonlashadi.
3. Meros — bitta o'q, kompozitsiya — ko'p o'q
# ⚠️ Meros bilan kombinatsiya portlashi
class Jonzot: ...
class UchuvchiJonzot(Jonzot): ...
class SuzuvchiJonzot(Jonzot): ...
class UchuvchiVaSuzuvchi(UchuvchiJonzot, SuzuvchiJonzot): ... # ko'p meros
class ZaharliUchuvchi(UchuvchiJonzot): ...
class ZaharliSuzuvchi(SuzuvchiJonzot): ...
class ZaharliUchuvchiVaSuzuvchi(...): ... # ⚠️ 2ⁿ sinf# ✅ Kompozitsiya — mustaqil o'lchamlar
@dataclass
class Jonzot:
harakat: list[Harakat] # uchish, suzish, yugurish
hujum: Hujum # zahar, tish, tirnoq
himoya: Himoya
burgut = Jonzot([Uchish()], Tirnoq(), Patlar())
ilon = Jonzot([Suralish()], Zahar(), Tangalar())
ajdaho = Jonzot([Uchish(), Suzish()], Zahar(), Tangalar()) # ✅ osonn ta xususiyat uchun meros 2ⁿ sinf talab qiladi; kompozitsiya — n ta sinf.
4. Meros — o'zgarishga qarshilik
# 1-yil: hammasi yaxshi
class Hisob:
def yech(self, n): ...
class OmonatHisob(Hisob): ...
class KreditHisob(Hisob): ...
# 2-yil: yangi talab
# "Kredit hisobdan manfiy balansgacha yechish mumkin"
# → Hisob ning invariantini buzadi
# → Butun ierarxiyani qayta loyihalash kerakKompozitsiya bilan:
class Hisob:
def __init__(self, qoida: BalansQoidasi):
self._qoida = qoida
def yech(self, n):
self._qoida.tekshir(self._balans, n) # ✅ qoida almashtiriladi
self._balans -= n5. Amaliy statistika
Katta kod bazalarida:
| Muammo | Meros | Kompozitsiya |
|---|---|---|
| "Bu metod qayerdan keldi?" | MRO bo'ylab qidirish | Aniq ko'rinadi |
| Test uchun almashtirish | patch kerak |
Konstruktorga uzatish |
| Yangi kombinatsiya | Yangi sinf | Yangi obyekt |
| Ota-sinf o'zgarishi | Avlodlar buziladi | API bir xil qolsa — ta'sir yo'q |
| Kod hajmi | Kam | Ko'proq (uzatish metodlari) |
6. Meros QACHON to'g'ri
Meros — yomon emas, faqat tor qo'llanish sohasi bor:
# ✅ 1. Haqiqiy "is-a" + shartnoma bir xil
class ValidationError(Exception): ...
class HttpXato(Exception): ...
# ✅ 2. Framework kengaytirish nuqtalari
class MeningKorinishim(django.views.View): ...
class MeningTestim(unittest.TestCase): ...
# ✅ 3. Abstrakt shartnoma (ABC/Protocol)
class Ombor(ABC):
@abstractmethod
def saqla(self, k, v): ...
class FaylOmbor(Ombor): ... # ✅ faqat shartnoma, kod emas
# ✅ 4. Mixin — bitta aniq imkoniyat
class JsonMixin:
def json(self): return json.dumps(self.lugat())
# ✅ 5. Enum, dataclass, NamedTuple
class Rang(Enum): ...Umumiy naqsh: meros interfeys uchun, kompozitsiya amalga oshirish uchun.
7. "Design Patterns" kitobidan
Gang of Four (1994), OOP naqshlari haqidagi asosiy kitob:
"Favor object composition over class inheritance." (Sinf merosidan ko'ra obyekt kompozitsiyasini afzal ko'ring.)
Ularning dalili:
- Meros kompilyatsiya vaqtida aniqlanadi, kompozitsiya — ish vaqtida
- Meros kapsulatsiyani buzadi (avlod ota-sinf ichini ko'radi)
- Meros ierarxiyasi qattiq, kompozitsiya moslashuvchan
Kitobdagi 23 naqshdan ko'pchiligi kompozitsiyaga asoslangan: Strategy, Decorator, Adapter, Composite, Bridge, Proxy, State, Command.
8. Zamonaviy tillar
Yangi tillar bu darsni tilga kiritdi:
| Til | Yondashuv |
|---|---|
| Go | Sinf merosi umuman yo'q — faqat interfeys va embedding |
| Rust | Meros yo'q — trait (interfeys) va kompozitsiya |
| Elixir | Meros yo'q — protokol va behaviour |
| Swift | Protocol-oriented programming rag'batlantiriladi |
| Kotlin | Sinflar sukut bo'yicha final — meros ochiq e'lon talab qiladi |
Go misoli:
type Yozuvchi interface {
Yoz(p []byte) (n int, err error)
}
type Jurnal struct {
chiqish Yozuvchi // kompozitsiya
prefiks string
}Go da meros umuman yo'q, lekin u — dunyodagi eng ko'p ishlatiladigan tillardan biri. Ya'ni meros majburiy emas.
Kotlin misoli:
class A { } // final — meros olib bo'lmaydi
open class B { } // ✅ ataylab ochiqBu — Joshua Bloch ning "Effective Java" maslahatidan:
"Design and document for inheritance or else prohibit it." (Meros uchun loyihalang va hujjatlang — yoki uni taqiqlang.)
9. Pythonda amaliy qoidalar
# ⭐ 1. Merosdan oldin savol bering
# "Bu sinf ota-sinfning HAR bir metodini ma'noli qo'llaydimi?"
# Yo'q → kompozitsiya
# ⭐ 2. O'rnatilgan turlardan meros olmang
class Sozlama(dict): ... # ⚠️ 40+ metod meros olinadi
class Sozlama(UserDict): ... # ✅ yoki
class Sozlama: # ✅
def __init__(self): self._d = {}
# ⭐ 3. Kompozitsiyada uzatishni yengillashtiring
class Kesh:
def __init__(self):
self._d = {}
def __getitem__(self, k): return self._d[k]
def __setitem__(self, k, v): self._d[k] = v
def __contains__(self, k): return k in self._d
def __len__(self): return len(self._d)
# ✅ Faqat KERAKLI metodlar
# ⭐ 4. Protocol bilan interfeys
from typing import Protocol
class Ombor(Protocol):
def saqla(self, k: str, v: str) -> None: ...
def ishla(o: Ombor): ... # ✅ meros TALAB QILINMAYDI10. Muvozanat
"Doim kompozitsiya" ham xato:
# ⚠️ Ortiqcha kompozitsiya
class Kvadrat:
def __init__(self):
self._shakl = Shakl()
def yuza(self): return self._shakl.yuza()
def perimetr(self): return self._shakl.perimetr()
def chiz(self): return self._shakl.chiz()
# ... 20 ta uzatish metodi ⚠️To'g'ri mezon:
| Holat | Tanlov |
|---|---|
| Barcha metodlar ma'noli + haqiqiy "is-a" | Meros |
| Faqat ba'zi metodlar kerak | Kompozitsiya |
| Ish vaqtida almashtirish kerak | Kompozitsiya |
| Bir necha mustaqil o'lcham | Kompozitsiya |
| Faqat shartnoma (kod yo'q) | ABC / Protocol |
| Bitta aniq imkoniyat qo'shish | Mixin |
Xulosa: maslahat "merosdan foydalanmang" emas, "birinchi navbatda kompozitsiyani ko'rib chiqing". Meros — kuchli vosita, lekin:
- U eng kuchli bog'lanishni yaratadi
- Ish vaqtida o'zgartirib bo'lmaydi
- Kombinatsiya portlashiga olib keladi
- Ota-sinf o'zgarishiga sezgir
Kompozitsiya ko'proq kod talab qiladi, lekin moslashuvchanroq va testlash osonroq. Meros — haqiqiy "is-a" va abstrakt shartnomalar uchun.
Nimani mustahkamlaydi: 2.2, 2.3, 2.7-bo'limlar.
Xulosa
Bu darsda meros asoslarini o'rgandik.
Eng muhim uch fikr:
Merosning yagona to'g'ri sababi — "is-a". "Bola bu bir turdagi Ota" jumlasi mantiqan to'g'ri bo'lishi kerak. Kod qayta ishlatish — meros sababi emas, bu kompozitsiya ishi. "Mashina — dvigatel" noto'g'ri, "mashinada dvigatel bor" to'g'ri.
Liskov almashtirish tamoyili — avlod ota-sinf o'rniga ishlashi kerak. Ota-sinf uchun yozilgan har test avlod bilan ham o'tishi shart. Buzilish belgilari: avlodda
NotImplementedError, qattiqlashtirilgan shartlar,isinstancetekshiruvlari. Kvadrat/To'rtburchak muammosi — o'zgarmaslik yoki birodar sinflar bilan hal qilinadi.Mo'rt asosiy sinf — merosning asosiy xavfi. Avlod ota-sinfning ichki chaqiruv tartibiga bog'liq bo'lib qoladi; ota-sinf o'zgarsa avlod jimgina buziladi. Yechimlar: xususiy metodlar (
__nom), shablon metod naqshi (aniq ilgaklar) yoki kompozitsiya.
Keyingi darsda super() ni chuqur ko'ramiz: u aslida nima qaytaradi, argumentsiz shakli qanday ishlaydi va ko'p merosda nima bo'ladi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!