IlmHamroh
Python kursi/OOP6/20-dars38 daqiqa
Mundarija (21)

8.6-dars: Meros asoslari

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


1. Kirish va motivatsiya

Uch sinf, uch marta takrorlangan kod:

python
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 qiladi

Meros bilan:

python
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 12

Lekin meros — eng ko'p noto'g'ri ishlatiladigan OOP vositasi:

python
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
  • isinstance va issubclass
  • Metodni kengaytirish va butunlay almashtirish
  • Liskov almashtirish tamoyili
  • Qachon meros, qachon kompozitsiya

2. Nazariya — chuqur tushuntirish

2.1. Sintaksis va asosiy tushunchalar

python
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'  ← yangi

Atamalar:

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:

python
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 turdagi Ota.

python
# ✅ To'g'ri
class Hayvon: ...
class It(Hayvon): ...           # It — bu Hayvon ✅

class Istisno(Exception): ...   # Istisno — bu Exception ✅
class Admin(Foydalanuvchi): ... # Admin — bu Foydalanuvchi ✅
python
# ❌ 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 BOR

Test: "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 — T ning avlodi bo'lsa, T turidagi obyektlarni S bilan almashtirish dasturning to'g'riligini buzmasligi kerak.

Oddiy tilda: avlod sinf ota-sinf o'rniga ishlashi kerak.

Klassik buzilish — Kvadrat/To'rtburchak:

python
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:

python
# 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 emas

LSP buzilishining belgilari:

python
# ⚠️ 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 buzadi

2.4. Metodni kengaytirish va almashtirish

A) Butunlay almashtirish:

python
class Ota:
    def f(self): return "ota"

class Bola(Ota):
    def f(self): return "bola"          # ota-sinf mantiqi ishlatilmaydi

B) Kengaytirish (super() bilan):

python
class Bola(Ota):
    def f(self):
        natija = super().f()            # ⭐ ota-sinf mantiqi
        return f"{natija} + bola"

Uch naqsh:

python
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 n

super() — 8.7-darsda batafsil.

2.5. isinstance va issubclass

python
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)           # True

Bir nechta tur:

python
isinstance(x, (int, float))     # ✅ ikkalasidan biri
isinstance(x, int | float)      # ✅ 3.10+

isinstance — oxirgi chora:

python
# ⚠️ 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:

python
# ✅ 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

python
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:

python
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:

  1. Chuqurlik ≤ 2-3 daraja. Chuqurroq — kompozitsiyani ko'rib chiqing.

  2. Ota-sinf — umumiy shartnoma. Avlodlar uni buzmasin.

  3. Faqat kod qayta ishlatish uchun meros olmang. Bu — kompozitsiya ishi.

  4. Ota-sinf o'zgarishi avlodlarni buzmasin (mo'rt asosiy sinf).

Mo'rt asosiy sinf muammosi:

python
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

python
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           → kompozitsiya

LSP

Avlod ota-sinf O'RNIGA ishlashi kerak.

Buzilish belgilari:
  • Avlodda NotImplementedError
  • Avlod shartni qattiqlashtiradi
  • isinstance tekshiruvlari paydo bo'ladi
  • Avlod invariantni buzadi

Tekshirish

python
isinstance(b, A)        ✅ meros bilan
issubclass(B, A)        sinflar uchun
type(b) is A            ⚠️ aniq tur
issubclass(B, B)        True — o'ziga ham

Loyihalash

Chuqurlik ≤ 2-3 daraja
Ota-sinf shartnomasini buzmang
Kod qayta ishlatish ≠ meros sababi
Ota-sinf ichki chaqiruvlarini hujjatlang

4. Batafsil misollar

Misol 1 — Meros asoslari

python
"""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:

text
=== 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        D

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

Misol 2 — "is-a" va Liskov tamoyili

python
"""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:

text
=== ⚠️ 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

python
"""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:

text
=== ⚠️ 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: int

Nima ko'rsatdi: 2.7-bo'lim.

Misol 4 — Amaliy: hujjat ierarxiyasi

python
"""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:

text
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'tdi

Nima 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

python
class B(A):
    def __init__(self, x, y):
        self.y = y                  # ⚠️
        super().__init__(x)         # ✅

2. "has-a" uchun meros

python
class Mashina(Dvigatel): ...        # ❌
class Mashina:
    def __init__(self):
        self.dvigatel = Dvigatel()  # ✅

3. Avlodda NotImplementedError

python
class Pingvin(Qush):
    def uch(self):
        raise NotImplementedError   # ⚠️ LSP buzilishi

4. isinstance bilan shoxlanish

python
if isinstance(s, Doira): ...        # ⚠️
return s.yuza()                     # ✅

5. Ota-sinf o'zgaruvchan atributini kengaytirish

python
class B(A):
    TEGLAR = A.TEGLAR
    TEGLAR.add("yangi")             # ⚠️ A niki ham o'zgardi
    TEGLAR = A.TEGLAR | {"yangi"}   # ✅

6. Chuqur ierarxiya

python
A → B → C → D → E → F               # ⚠️
A → B  +  kompozitsiya              # ✅

7. dict/list dan meros

python
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

python
def kop_qosh(self, xs):
    self.soni += len(xs)
    super().kop_qosh(xs)            # ⚠️ ichida qosh() chaqirilishi mumkin

7. 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

  1. "X — bu Y" testini o'tkazing. O'tmasa — kompozitsiya.

  2. super().__init__() ni doim chaqiring. Ota-sinf holati kerak.

  3. Chuqurlik ≤ 2-3 daraja. Chuqurroq — qayta ko'rib chiqing.

  4. Ota-sinf shartnomasini hujjatlang. Nima kafolatlanadi, nima kengaytiriladi.

  5. Kengaytirish nuqtalarini aniq belgilang. Shablon metod naqshi.

  6. Ota-sinf testlarini avlodlarda ham ishlating. LSP tekshiruvi.

  7. isinstance o'rniga polimorfizm. Tur bo'yicha shoxlanish — antipattern.

  8. O'rnatilgan turlardan meros olmang. UserDict, UserList yoki kompozitsiya.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
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
  1. A — meros olindi
  2. False — super().__init__() chaqirilmadi
  3. 1 2 — B.__dict__ ga yozildi
  4. True False True
  5. AB — kengaytirish
  6. (<class 'object'>,) (<class '__main__.A'>, <class 'object'>)
  7. A B — type(self) haqiqiy sinf
  8. B — cls haqiqiy sinf
  9. [1] — bir ro'yxat
  10. B.g — mo'rt asosiy sinf mexanizmi
  11. True True
  12. ['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 Hujjat
Javoblar
  1. Kompozitsiya — mashinada dvigatel bor
  2. Meros — talaba bu odam
  3. Kompozitsiya — stack ichida list bor (list ning barcha metodlari kerak emas)
  4. Meros — admin bu foydalanuvchi
  5. Kompozitsiya — buyurtmada mahsulotlar bor
  6. Ikkalasi ham emas — birodar sinflar (umumiy Shakl ota-sinfi)
  7. Meros — ValidationError bu Exception
  8. Kompozitsiya yoki UserDict — kesh dict emas (chegara, TTL bor)
  9. Kompozitsiya — uyda xonalar bor
  10. Meros — JSON hujjat bu hujjat

Vazifa 3: Xodim ierarxiyasi

Yozing:

  1. Xodim — ism, maosh, oylik(), tavsif()
  2. Menejer(Xodim) — jamoa uchun ustama
  3. Sotuvchi(Xodim) — sotuvdan foiz
  4. Stajyor(Xodim) — maoshning 50%
  5. Barcha sinflar uchun bir xil test to'plami (LSP)
  6. isinstance ishlatmasdan umumiy hisobot funksiyasi

Vazifa 4: LSP buzilishi detektori

Vosita yozing:

  1. Sinf ierarxiyasini skanerlasin
  2. Avlodda NotImplementedError tashlaydiganlarni topsin
  3. Avlod qo'shimcha tekshiruv qo'shganini topsin
  4. Qaytarish turi mos kelmasligini
  5. super().__init__() chaqirilmaganini
  6. Har biri uchun tuzatish taklifi

Vazifa 5: Shablon metod naqshi

MalumotQuvuri abstrakt sinfi:

  1. bajar() — algoritmni boshqaradi (qayta aniqlanmasin)
  2. Ilgaklar: _yukla(), _tekshir(), _ozgartir(), _saqla()
  3. Ixtiyoriy ilgaklar: _boshlashdan_oldin(), _tugagandan_keyin()
  4. Uchta avlod: CSV, JSON, XML
  5. Xato bo'lsa _xato_yuz_berdi() chaqirilsin
  6. Har bosqich vaqtini o'lchang

Vazifa 6: Mo'rt asosiy sinf

  1. Ota-sinf yozing, unda f() g() ni chaqirsin
  2. Avlod g() ni qayta aniqlasin — muammoni ko'rsating
  3. Xususiy metod bilan tuzating
  4. Kompozitsiya bilan tuzating
  5. Shablon metod bilan tuzating
  6. 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):

python
# 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:

  • B ning barcha ommaviy metodlarini oladi
  • B ning barcha himoyalangan (_) a'zolarini ko'radi
  • B ning ichki chaqiruv tartibiga bog'liq bo'ladi
  • B ning invariantlariga rioya qilishi kerak
  • B o'zgarsa — buzilishi mumkin

Bu — "banan so'radingiz, gorilla va o'rmon oldingiz" muammosi (Joe Armstrong).

2. Meros — kompilyatsiya vaqtida qotadi

python
# ⚠️ 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 qatlami

Bu — bog'liqlikni kiritish (dependency injection). Testlash osonlashadi.

3. Meros — bitta o'q, kompozitsiya — ko'p o'q

python
# ⚠️ 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
python
# ✅ 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())   # ✅ oson

n ta xususiyat uchun meros 2ⁿ sinf talab qiladi; kompozitsiya — n ta sinf.

4. Meros — o'zgarishga qarshilik

python
# 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 kerak

Kompozitsiya bilan:

python
class Hisob:
    def __init__(self, qoida: BalansQoidasi):
        self._qoida = qoida

    def yech(self, n):
        self._qoida.tekshir(self._balans, n)    # ✅ qoida almashtiriladi
        self._balans -= n

5. 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:

python
# ✅ 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:

  1. Meros kompilyatsiya vaqtida aniqlanadi, kompozitsiya — ish vaqtida
  2. Meros kapsulatsiyani buzadi (avlod ota-sinf ichini ko'radi)
  3. 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:

go
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:

kotlin
class A { }                     // final — meros olib bo'lmaydi
open class B { }                // ✅ ataylab ochiq

Bu — 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

python
# ⭐ 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 QILINMAYDI

10. Muvozanat

"Doim kompozitsiya" ham xato:

python
# ⚠️ 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:

  1. U eng kuchli bog'lanishni yaratadi
  2. Ish vaqtida o'zgartirib bo'lmaydi
  3. Kombinatsiya portlashiga olib keladi
  4. 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:

  1. 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.

  2. 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, isinstance tekshiruvlari. Kvadrat/To'rtburchak muammosi — o'zgarmaslik yoki birodar sinflar bilan hal qilinadi.

  3. 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.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.6-dars: Meros asoslari — IlmHamroh