IlmHamroh
Python kursi/OOP20/20-dars41 daqiqa
Mundarija (22)

8.20-dars: Kompozitsiya vs meros

8-QISM — OBYEKTGA YO'NALTIRILGAN DASTURLASH · 20-dars · 8-QISM YAKUNI


1. Kirish va motivatsiya

Bir vazifa, ikki yechim:

python
# Meros
class Stack(list):
    def push(self, x): self.append(x)


s = Stack()
s.push(1)
s.append(2)                     # ⚠️ ruxsat berildi
s.insert(0, 3)                  # ⚠️ stack'ning ma'nosi buzildi
s[0] = 99                       # ⚠️
s.sort()                        # ⚠️
len(dir(s))                     # 46 metod — 40 tasi keraksiz
python
# Kompozitsiya
class Stack:
    def __init__(self): self._el = []
    def push(self, x): self._el.append(x)
    def pop(self): return self._el.pop()
    def __len__(self): return len(self._el)


s = Stack()
s.push(1)
s.append(2)                     # ✅ AttributeError — stack'da append yo'q
len(dir(s))                     # 8 metod — faqat kerakli

Meros hamma narsani beradi; kompozitsiya faqat kerakni.

Bu darsda:

  • "is-a" vs "has-a" — qaror mezoni
  • Kompozitsiyaning oltita naqshi
  • Kombinatsiya portlashi va uning yechimi
  • Uzatish (delegation) va __getattr__
  • Qachon meros to'g'ri
  • 8-qism yakuni — barcha bilimlarni bog'lash

2. Nazariya — chuqur tushuntirish

2.1. Uch savol

1. "X — bu Y mi?" (is-a)

python
class ValidationError(Exception): ...       # ✅ "ValidationError — bu Exception"
class Admin(Foydalanuvchi): ...             # ✅ "Admin — bu Foydalanuvchi"
class Mashina(Dvigatel): ...                # ❌ "Mashina — bu Dvigatel"?

2. "X da Y bormi?" (has-a)

python
class Mashina:
    def __init__(self):
        self.dvigatel = Dvigatel()          # ✅ "mashinada dvigatel bor"

3. "X ota-sinfning HAR metodini ma'noli qo'llaydimi?"

python
class Stack(list): ...
# list.sort()    → stack uchun ma'nosizmi? ✅ ha
# list.insert()  → ma'nosizmi? ✅ ha
# → MEROS NOTO'G'RI

Uchinchi savol — eng amaliy.

2.2. Kompozitsiyaning oltita naqshi

A) Oddiy egalik:

python
class Mashina:
    def __init__(self):
        self.dvigatel = Dvigatel()
        self.gildiraklar = [Gildirak() for _ in range(4)]

B) Strategiya (Strategy) — ish vaqtida almashtirish:

python
class Saralovchi:
    def __init__(self, algoritm):
        self._algoritm = algoritm

    def sarala(self, m):
        return self._algoritm(m)


Saralovchi(sorted)
Saralovchi(mening_saralashim)   # ⭐ ish vaqtida almashtirildi

C) Uzatish (delegation) — tanlangan metodlar:

python
class Stack:
    def __init__(self):
        self._el = []

    def push(self, x): self._el.append(x)       # ⭐ uzatiladi
    def pop(self): return self._el.pop()
    def __len__(self): return len(self._el)
    # ⭐ sort, insert, __setitem__ — YO'Q

D) Bezak (Decorator) — qatlam qo'shish:

python
class KeshlanganOmbor:
    def __init__(self, ichki: Ombor):
        self._ichki = ichki
        self._kesh = {}

    def olish(self, k):
        if k not in self._kesh:
            self._kesh[k] = self._ichki.olish(k)
        return self._kesh[k]

E) Adapter — interfeysni moslashtirish:

python
class EskiAdapter(YangiInterfeys):
    def __init__(self, eski):
        self._eski = eski

    def yangi_metod(self, x):
        return self._eski.eski_metod(x)     # ⭐ moslashtirildi

F) Fasad (Facade) — murakkablikni yashirish:

python
class Kutubxona:
    def __init__(self):
        self._db = Baza()
        self._kesh = Kesh()
        self._jurnal = Jurnal()

    def kitob_top(self, isbn):              # ⭐ oddiy interfeys
        ...

2.3. Kombinatsiya portlashi

Meros bilan:

python
class Jonzot: ...
class Uchuvchi(Jonzot): ...
class Suzuvchi(Jonzot): ...
class UchuvchiVaSuzuvchi(Uchuvchi, Suzuvchi): ...
class Zaharli(Jonzot): ...
class ZaharliUchuvchi(Zaharli, Uchuvchi): ...
class ZaharliSuzuvchi(Zaharli, Suzuvchi): ...
class ZaharliUchuvchiVaSuzuvchi(...): ...

n xususiyat → 2ⁿ sinf. 10 xususiyat = 1024 sinf.

Kompozitsiya bilan:

python
@dataclass
class Jonzot:
    harakat: list[Harakat]
    hujum: Hujum
    himoya: Himoya


burgut = Jonzot([Uchish()], Tirnoq(), Patlar())
ilon = Jonzot([Suralish()], Zahar(), Tangalar())
ajdaho = Jonzot([Uchish(), Suzish()], Zahar(), Tangalar())

n xususiyat → n sinf. Yangi kombinatsiya — yangi obyekt, yangi sinf emas.

2.4. Uzatish usullari

A) Qo'lda (aniq, tavsiya):

python
class Stack:
    def push(self, x): self._el.append(x)
    def pop(self): return self._el.pop()

Aniq, IDE tushunadi, mypy tekshiradi.

B) __getattr__ (avtomatik, ehtiyot):

python
class Oram:
    def __init__(self, ichki):
        self._ichki = ichki

    def __getattr__(self, nom):
        return getattr(self._ichki, nom)    # ⚠️ HAMMASINI uzatadi

Muammolar:

  • Dunder metodlar uzatilmaydi (len(), +, [])
  • IDE va mypy bilmaydi
  • Xato nomlar ham uzatiladi
  • Nosozlikni topish qiyin

C) Dinamik yaratish:

python
def uzat(*nomlar):
    def dekorator(cls):
        for nom in nomlar:
            setattr(cls, nom, _uzatuvchi_yasa(nom))
        return cls
    return dekorator


@uzat("append", "pop", "__len__")
class Stack: ...

Dunder metodlarni ham qamraydi.

2.5. Meros qachon to'g'ri

Beshta holat:

python
# 1. Haqiqiy "is-a" + bir xil shartnoma
class ValidationError(Exception): ...

# 2. Abstrakt interfeys (ABC/Protocol)
class FaylOmbor(Ombor): ...

# 3. Framework kengaytirish nuqtalari
class MeningKorinishim(django.views.View): ...
class MeningTestim(unittest.TestCase): ...

# 4. Mixin — bitta aniq imkoniyat
class Model(JsonMixin, TenglikMixin, Asos): ...

# 5. Til konstruktsiyalari
class Rang(Enum): ...
@dataclass
class Nuqta: ...
class Nuqta(NamedTuple): ...

Naqsh: meros interfeys uchun, kompozitsiya amalga oshirish uchun.

2.6. Solishtirish

Mezon Meros Kompozitsiya
Bog'lanish Eng kuchli Kuchsiz
Ish vaqtida o'zgartirish
Kombinatsiyalar 2ⁿ sinf n sinf
Kod hajmi Kam Ko'proq
isinstance (yoki Protocol)
Testlash patch Konstruktorga uzatish
Ota-sinf o'zgarishi Buzadi API bir xil qolsa — yo'q
"Bu metod qayerdan?" MRO qidiruv Aniq

2.7. Amaliy qaror daraxti

1. "X — bu Y mi?" — mantiqan to'g'rimi?
   ❌ → KOMPOZITSIYA

2. Y ning HAR metodi X uchun ma'nolimi?
   ❌ → KOMPOZITSIYA

3. Ish vaqtida almashtirish kerakmi?
   ✅ → KOMPOZITSIYA (strategiya)

4. Bir necha mustaqil o'lcham bormi?
   ✅ → KOMPOZITSIYA

5. Y — abstrakt interfeysmi (ABC/Protocol)?
   ✅ → MEROS

6. Y — framework kengaytirish nuqtasimi?
   ✅ → MEROS

7. Y — bitta imkoniyat qo'shadigan mixinmi?
   ✅ → MEROS

8. Aks holda → KOMPOZITSIYA (sukut)

3. Tez ma'lumotnoma

Uch savol

1. "X — bu Y mi?"                       → meros
2. "X da Y bormi?"                      → kompozitsiya
3. Y ning HAR metodi ma'nolimi?         → yo'q bo'lsa kompozitsiya

Kompozitsiya naqshlari

Egalik        self.dvigatel = Dvigatel()
Strategiya    self._algoritm = f          ish vaqtida almashtiriladi
Uzatish       def push(self, x): self._el.append(x)
Bezak         self._ichki = Ombor()       qatlam qo'shadi
Adapter       eski → yangi interfeys
Fasad         murakkablikni yashiradi

Kombinatsiya portlashi

Meros:        n xususiyat → 2ⁿ sinf
Kompozitsiya: n xususiyat → n sinf

Meros to'g'ri bo'lgan 5 holat

1. Haqiqiy "is-a" + bir xil shartnoma
2. ABC / Protocol
3. Framework kengaytirish nuqtasi
4. Mixin (bitta imkoniyat)
5. Enum, dataclass, NamedTuple

4. Batafsil misollar

Misol 1 — Ikki yondashuv yonma-yon

python
"""Bir vazifa — meros va kompozitsiya."""

from __future__ import annotations

print("=== ⚠️ 1. list dan meros ===\n")


class StackMeros(list):
    """⚠️ list ning BARCHA metodlari meros olinadi."""

    def push(self, x):
        self.append(x)

    def top(self):
        if not self:
            raise IndexError("Stack bo'sh")
        return self[-1]


class StackKompozitsiya:
    """✅ Faqat KERAKLI metodlar."""

    __slots__ = ("_el",)

    def __init__(self, el=()):
        self._el = list(el)

    def push(self, x) -> None:
        self._el.append(x)

    def pop(self):
        if not self._el:
            raise IndexError("Stack bo'sh")
        return self._el.pop()

    def top(self):
        if not self._el:
            raise IndexError("Stack bo'sh")
        return self._el[-1]

    def __len__(self): return len(self._el)
    def __bool__(self): return bool(self._el)
    def __iter__(self): return reversed(self._el)   # ⭐ LIFO tartibi
    def __repr__(self): return f"Stack({self._el!r})"


sm, sk = StackMeros(), StackKompozitsiya()
for s in (sm, sk):
    s.push(1); s.push(2); s.push(3)

print(f"  StackMeros:        {sm}")
print(f"  StackKompozitsiya: {sk}\n")

print(f"  Ommaviy metodlar soni:")
print(f"    StackMeros:        "
      f"{len([m for m in dir(sm) if not m.startswith('_')])}")
print(f"    StackKompozitsiya: "
      f"{len([m for m in dir(sk) if not m.startswith('_')])}")

print(f"\n  ⚠️ StackMeros da ma'nosiz amallar:")
BUZUVCHILAR = [
    ("sm.insert(0, 99)",    lambda: sm.insert(0, 99)),
    ("sm.sort()",           lambda: sm.sort()),
    ("sm[0] = 999",         lambda: sm.__setitem__(0, 999)),
    ("sm.reverse()",        lambda: sm.reverse()),
    ("sm.remove(2)",        lambda: sm.remove(2)),
    ("del sm[0]",           lambda: sm.__delitem__(0)),
    ("sm.extend([7, 8])",   lambda: sm.extend([7, 8])),
]

for kod, f in BUZUVCHILAR:
    try:
        f()
        n = f"⚠️ o'tdi → {list(sm)}"
    except Exception as e:
        n = f"✅ {type(e).__name__}"
    print(f"    {kod:<22} {n}")

print(f"\n  ✅ StackKompozitsiya da:")
for kod, m in [("sk.insert(0, 99)", "insert"), ("sk.sort()", "sort"),
               ("sk[0] = 999", "__setitem__"),
               ("sk.extend([7])", "extend")]:
    bor = hasattr(sk, m)
    print(f"    {kod:<22} {'⚠️ bor' if bor else '✅ AttributeError'}")

print(f"""
  ⭐ MEZON: "list ning HAR metodi Stack uchun ma'nolimi?"

     append   ✅  (lekin push nomi yaxshiroq)
     pop      ✅
     insert   ❌  stack o'rtasiga qo'shib bo'lmaydi
     sort     ❌  tartib — stack mohiyati
     __setitem__ ❌
     reverse  ❌

     → Javob YO'Q → KOMPOZITSIYA
""")


print("=== ⭐ 2. LIFO tartibi ===\n")

sm2, sk2 = StackMeros(), StackKompozitsiya()
for s in (sm2, sk2):
    s.push("a"); s.push("b"); s.push("c")

print(f"  push: a, b, c\n")
print(f"    list(StackMeros):        {list(sm2)}   ⚠️ FIFO")
print(f"    list(StackKompozitsiya): {list(sk2)}   ✅ LIFO")

print(f"\n  for bilan:")
print(f"    Meros:        ", end="")
print(" → ".join(str(x) for x in sm2))
print(f"    Kompozitsiya: ", end="")
print(" → ".join(str(x) for x in sk2))

print(f"""
  ⭐ Kompozitsiya bilan __iter__ ni STACK MANTIQIGA
     moslashtira oldik. Meros bilan list.__iter__
     meros olinadi va uni o'zgartirish CHALKASH bo'lardi.
""")


print("=== 3. Uchinchi variant: UserList ===\n")

from collections import UserList


class StackUser(UserList):
    """⚠️ Hali ham list interfeysi."""

    def push(self, x): self.data.append(x)


su = StackUser()
su.push(1); su.push(2)
print(f"  StackUser: {su}")
print(f"    su.sort bor: {hasattr(su, 'sort')}   ⚠️")
print(f"    su.insert bor: {hasattr(su, 'insert')}   ⚠️")

print(f"""
  ⚠️ UserList ham list interfeysini beradi —
     faqat meros MUAMMOSINI (C metodlari) hal qiladi,
     ORTIQCHA METODLAR muammosini emas.

  ⭐ UserList: dict/list ni KENGAYTIRISH uchun
     Kompozitsiya: CHEKLASH uchun
""")


print("=== ✅ 4. Meros TO'G'RI bo'lgan holat ===\n")

from collections.abc import MutableSequence


class TekshirilganRoyxat(MutableSequence):
    """✅ Haqiqiy 'is-a' — ro'yxat, lekin tekshiruv bilan."""

    def __init__(self, tur: type, el=()):
        self._tur = tur
        self._el = []
        self.extend(el)

    def _tekshir(self, x):
        if not isinstance(x, self._tur):
            raise TypeError(
                f"{self._tur.__name__} kutildi, "
                f"{type(x).__name__} keldi"
            )
        return x

    def __getitem__(self, i): return self._el[i]
    def __len__(self): return len(self._el)
    def __delitem__(self, i): del self._el[i]

    def __setitem__(self, i, q):
        if isinstance(i, slice):
            self._el[i] = [self._tekshir(x) for x in q]
        else:
            self._el[i] = self._tekshir(q)

    def insert(self, i, q):
        self._el.insert(i, self._tekshir(q))

    def __repr__(self):
        return f"TekshirilganRoyxat({self._tur.__name__}, {self._el!r})"


t = TekshirilganRoyxat(int, [1, 2, 3])
print(f"  {t}\n")

print(f"  Barcha list metodlari ishlaydi:")
for kod, f in [
    ("t.append(4)",     lambda: t.append(4)),
    ("t.insert(0, 0)",  lambda: t.insert(0, 0)),
    ("t.extend([5,6])", lambda: t.extend([5, 6])),
    ("t[1] = 99",       lambda: t.__setitem__(1, 99)),
    ("t.pop()",         lambda: t.pop()),
    ("t.remove(99)",    lambda: t.remove(99)),
    ("t.reverse()",     lambda: t.reverse()),
    ("3 in t",          lambda: 3 in t),
    ("t.index(2)",      lambda: t.index(2)),
]:
    natija = f()
    korinish = f" → {natija!r}" if natija is not None else ""
    print(f"    {kod:<20}{korinish:<12} {list(t)}")

print(f"\n  Tekshiruv:")
for kod, f in [
    ("t.append('matn')",    lambda: t.append("matn")),
    ("t[0] = 3.14",         lambda: t.__setitem__(0, 3.14)),
    ("t.insert(0, None)",   lambda: t.insert(0, None)),
]:
    try:
        f(); n = "⚠️ o'tdi"
    except TypeError as e:
        n = f"✅ TypeError: {e}"
    print(f"    {kod:<22} {n}")

print(f"""
  ⭐ NEGA BU YERDA MEROS TO'G'RI:

     1. "TekshirilganRoyxat — bu MutableSequence" ✅
     2. HAR metod ma'noli (append, insert, sort, ...)
     3. Shartnoma buzilmaydi (LSP ✅)
     4. Faqat qo'shimcha tekshiruv qo'shildi
     5. isinstance(t, MutableSequence) → True (foydali)

  ⚠️ Stack da esa metodlarning YARMI ma'nosiz edi.
""")

print(f"  isinstance tekshiruvi:")
from collections.abc import Sequence
for nom, o in [("StackMeros", sm), ("StackKompozitsiya", sk),
               ("TekshirilganRoyxat", t)]:
    print(f"    {nom:<22} Sequence: {isinstance(o, Sequence)}")

Natijaning muhim qismi:

text
=== ⚠️ 1. list dan meros ===

  Ommaviy metodlar soni:
    StackMeros:        13
    StackKompozitsiya: 3

  ⚠️ StackMeros da ma'nosiz amallar:
    sm.insert(0, 99)       ⚠️ o'tdi → [99, 1, 2, 3]
    sm.sort()              ⚠️ o'tdi → [1, 2, 3, 99]
    sm[0] = 999            ⚠️ o'tdi → [999, 2, 3, 99]

  ✅ StackKompozitsiya da:
    sk.insert(0, 99)       ✅ AttributeError
    sk.sort()              ✅ AttributeError

=== ⭐ 2. LIFO tartibi ===

    list(StackMeros):        ['a', 'b', 'c']   ⚠️ FIFO
    list(StackKompozitsiya): ['c', 'b', 'a']   ✅ LIFO

=== ✅ 4. Meros TO'G'RI bo'lgan holat ===

    t.append('matn')       ✅ TypeError: int kutildi, str keldi
    t[0] = 3.14            ✅ TypeError: int kutildi, float keldi

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

Misol 2 — Kombinatsiya portlashi

python
"""n xususiyat: 2ⁿ sinf vs n sinf."""

from __future__ import annotations

import itertools
from abc import ABC, abstractmethod
from dataclasses import dataclass, field

print("=== ⚠️ 1. Meros bilan portlash ===\n")


class Jonzot:
    def __init__(self, nom): self.nom = nom
    def tavsif(self): return self.nom


class Uchuvchi(Jonzot):
    def uch(self): return f"{self.nom} uchdi"
    def tavsif(self): return f"{super().tavsif()} (uchadi)"


class Suzuvchi(Jonzot):
    def suz(self): return f"{self.nom} suzdi"
    def tavsif(self): return f"{super().tavsif()} (suzadi)"


class Zaharli(Jonzot):
    def zahar(self): return f"{self.nom} zaharladi"
    def tavsif(self): return f"{super().tavsif()} (zaharli)"


class UchuvchiSuzuvchi(Uchuvchi, Suzuvchi): pass
class ZaharliUchuvchi(Zaharli, Uchuvchi): pass
class ZaharliSuzuvchi(Zaharli, Suzuvchi): pass
class ZaharliUchuvchiSuzuvchi(Zaharli, Uchuvchi, Suzuvchi): pass


SINFLAR = [Jonzot, Uchuvchi, Suzuvchi, Zaharli, UchuvchiSuzuvchi,
           ZaharliUchuvchi, ZaharliSuzuvchi, ZaharliUchuvchiSuzuvchi]

print(f"  3 xususiyat → {len(SINFLAR)} sinf\n")
for S in SINFLAR:
    print(f"    {S.__name__}")

print(f"\n  ⚠️ Formula: n xususiyat → 2ⁿ sinf\n")
print(f"  {'Xususiyatlar':<16} {'Sinflar':<12} {'Izoh'}")
print("  " + "─" * 46)
for n in range(1, 11):
    sinflar = 2 ** n
    izoh = ("" if sinflar < 16 else
            "⚠️ ko'p" if sinflar < 128 else "❌ boshqarib bo'lmaydi")
    print(f"  {n:<16} {sinflar:<12} {izoh}")

print(f"\n  Yangi xususiyat qo'shsak (masalan, 'Yuguruvchi'):")
print(f"    Yana {len(SINFLAR)} ta sinf kerak bo'ladi → "
      f"{len(SINFLAR) * 2} ta")

z = ZaharliUchuvchiSuzuvchi("Ajdaho")
print(f"\n  ZaharliUchuvchiSuzuvchi('Ajdaho'):")
print(f"    tavsif: {z.tavsif()}")
print(f"    MRO: {' → '.join(c.__name__ for c in type(z).__mro__[:-1])}")


print("\n\n=== ✅ 2. Kompozitsiya ===\n")


class Harakat(ABC):
    @abstractmethod
    def harakatlan(self, nom: str) -> str: ...

    @property
    @abstractmethod
    def tezlik(self) -> float: ...


class Uchish(Harakat):
    def harakatlan(self, nom): return f"{nom} uchdi"
    @property
    def tezlik(self): return 50.0


class Suzish(Harakat):
    def harakatlan(self, nom): return f"{nom} suzdi"
    @property
    def tezlik(self): return 15.0


class Yugurish(Harakat):
    def harakatlan(self, nom): return f"{nom} yugurdi"
    @property
    def tezlik(self): return 30.0


class Suralish(Harakat):
    def harakatlan(self, nom): return f"{nom} suralib bordi"
    @property
    def tezlik(self): return 5.0


class Hujum(ABC):
    @abstractmethod
    def hujum(self, nom: str) -> str: ...

    @property
    @abstractmethod
    def zarar(self) -> int: ...


class Zahar(Hujum):
    def hujum(self, nom): return f"{nom} zaharladi"
    @property
    def zarar(self): return 30


class Tish(Hujum):
    def hujum(self, nom): return f"{nom} tishladi"
    @property
    def zarar(self): return 20


class Tirnoq(Hujum):
    def hujum(self, nom): return f"{nom} tirnadi"
    @property
    def zarar(self): return 15


class HujumYoq(Hujum):
    def hujum(self, nom): return f"{nom} qochdi"
    @property
    def zarar(self): return 0


@dataclass
class Jonzot2:
    """⭐ Xususiyatlar — MAYDONLAR, sinflar emas."""

    nom: str
    harakatlar: list[Harakat] = field(default_factory=list)
    hujum_usuli: Hujum = field(default_factory=HujumYoq)
    jon: int = 100

    def harakatlan(self) -> list[str]:
        return [h.harakatlan(self.nom) for h in self.harakatlar]

    def hujum(self) -> str:
        return self.hujum_usuli.hujum(self.nom)

    @property
    def maks_tezlik(self) -> float:
        return max((h.tezlik for h in self.harakatlar), default=0.0)

    @property
    def zarar(self) -> int:
        return self.hujum_usuli.zarar

    def tavsif(self) -> str:
        xus = [type(h).__name__.lower() for h in self.harakatlar]
        xus.append(type(self.hujum_usuli).__name__.lower())
        return f"{self.nom} ({', '.join(xus)})"


JONZOTLAR = [
    Jonzot2("Burgut", [Uchish()], Tirnoq()),
    Jonzot2("Ilon", [Suralish()], Zahar()),
    Jonzot2("Ajdaho", [Uchish(), Suzish(), Yugurish()], Zahar(), jon=500),
    Jonzot2("Quyon", [Yugurish()], HujumYoq(), jon=30),
    Jonzot2("O'rdak", [Uchish(), Suzish(), Yugurish()], Tish()),
    Jonzot2("Akula", [Suzish()], Tish(), jon=200),
]

print(f"  Sinflar soni:")
print(f"    Harakat turlari: 4 (Uchish, Suzish, Yugurish, Suralish)")
print(f"    Hujum turlari:   4 (Zahar, Tish, Tirnoq, HujumYoq)")
print(f"    Jonzot:          1")
print(f"    ⭐ JAMI: 9 sinf\n")

print(f"  {'Jonzot':<32} {'Tezlik':>8} {'Zarar':>7} {'Jon'}")
print("  " + "─" * 58)
for j in JONZOTLAR:
    print(f"  {j.tavsif():<32} {j.maks_tezlik:>8.0f} "
          f"{j.zarar:>7} {j.jon:>5}")

print(f"\n  Amallar:")
for j in JONZOTLAR[:3]:
    print(f"    {j.nom}:")
    for h in j.harakatlan():
        print(f"      {h}")
    print(f"      {j.hujum()}")

print(f"""
  ⭐ MUMKIN BO'LGAN KOMBINATSIYALAR:
     Harakatlar: 2^4 - 1 = 15 (bo'sh bo'lmagan to'plamlar)
     Hujumlar:   4
     ⭐ JAMI: 15 × 4 = 60 xil jonzot — 9 SINF bilan

  ⚠️ Meros bilan: 60 sinf kerak bo'lardi
""")

print(f"  Barcha kombinatsiyalar (birinchi 8 tasi):")
HARAKATLAR = [Uchish, Suzish, Yugurish, Suralish]
HUJUMLAR = [Zahar, Tish, Tirnoq, HujumYoq]

kombinatsiyalar = []
for n in range(1, 3):
    for h_kombo in itertools.combinations(HARAKATLAR, n):
        for hujum in HUJUMLAR:
            kombinatsiyalar.append((h_kombo, hujum))

for h_kombo, hujum in kombinatsiyalar[:8]:
    j = Jonzot2("Test", [H() for H in h_kombo], hujum())
    xus = "+".join(H.__name__ for H in h_kombo)
    print(f"    {xus:<24} {hujum.__name__:<12} "
          f"tezlik={j.maks_tezlik:.0f}, zarar={j.zarar}")

print(f"\n  ⭐ Jami mumkin: {len(kombinatsiyalar)}+ kombinatsiya")


print("\n\n=== ⭐ 3. Ish vaqtida o'zgartirish ===\n")

j = Jonzot2("Kuchukcha", [Yugurish()], Tish(), jon=50)
print(f"  Boshida: {j.tavsif()}, tezlik={j.maks_tezlik}")

j.harakatlar.append(Suzish())
print(f"\n  Suzishni o'rgandi:")
print(f"    {j.tavsif()}")
for h in j.harakatlan():
    print(f"      {h}")

j.hujum_usuli = Zahar()
print(f"\n  Zaharli bo'ldi:")
print(f"    {j.tavsif()}, zarar={j.zarar}")

j.harakatlar = [Uchish()]
print(f"\n  Faqat uchadigan bo'ldi:")
print(f"    {j.tavsif()}, tezlik={j.maks_tezlik}")

print(f"""
  ⚠️ MEROS bilan bu MUMKIN EMAS:
     obyekt yaratilgandan keyin sinfini o'zgartirib bo'lmaydi
     (__class__ ni o'zgartirish — xavfli hack)

  ✅ KOMPOZITSIYA bilan — oddiy atribut o'zgarishi
""")


print("=== 4. Yangi xususiyat qo'shish ===\n")


class Kovlash(Harakat):
    """⭐ Yangi harakat — MAVJUD KOD O'ZGARMAYDI."""
    def harakatlan(self, nom): return f"{nom} yer kovladi"
    @property
    def tezlik(self): return 2.0


class Otashnafas(Hujum):
    """⭐ Yangi hujum."""
    def hujum(self, nom): return f"{nom} olov purkadi"
    @property
    def zarar(self): return 80


ajdaho = Jonzot2("Buyuk Ajdaho",
                 [Uchish(), Suzish(), Kovlash()],
                 Otashnafas(), jon=1000)

print(f"  Ikki yangi sinf qo'shildi: Kovlash, Otashnafas\n")
print(f"    {ajdaho.tavsif()}")
print(f"    tezlik={ajdaho.maks_tezlik}, zarar={ajdaho.zarar}")
for h in ajdaho.harakatlan():
    print(f"      {h}")
print(f"      {ajdaho.hujum()}")

print(f"""
  ⭐ QO'SHILDI: 2 sinf
  ⭐ O'ZGARDI:  0 qator mavjud kod
  ⭐ YANGI KOMBINATSIYALAR: 2^5 × 5 = 160

  ⚠️ Meros bilan: 60 → 160 sinf (100 ta yangi)
""")


print("=== 5. Jang simulyatsiyasi ===\n")


def jang(a: Jonzot2, b: Jonzot2, maks_raund: int = 20) -> str:
    """⭐ Aniq turlarni bilmaydi."""
    ja, jb = a.jon, b.jon
    # Tezroq birinchi hujum qiladi
    if b.maks_tezlik > a.maks_tezlik:
        a, b = b, a
        ja, jb = jb, ja

    for raund in range(1, maks_raund + 1):
        jb -= a.zarar
        if jb <= 0:
            return f"{a.nom} g'alaba qozondi ({raund}-raund)"
        ja -= b.zarar
        if ja <= 0:
            return f"{b.nom} g'alaba qozondi ({raund}-raund)"
    return f"Durang ({maks_raund} raund)"


JANGLAR = [
    (JONZOTLAR[0], JONZOTLAR[1]),
    (JONZOTLAR[2], JONZOTLAR[5]),
    (JONZOTLAR[3], JONZOTLAR[4]),
    (ajdaho, JONZOTLAR[2]),
]

for a, b in JANGLAR:
    print(f"  {a.nom} (jon={a.jon}, zarar={a.zarar}) vs "
          f"{b.nom} (jon={b.jon}, zarar={b.zarar})")
    print(f"    → {jang(a, b)}")

Natijaning muhim qismi:

text
=== ⚠️ 1. Meros bilan portlash ===

  3 xususiyat → 8 sinf

  Xususiyatlar     Sinflar      Izoh
  ──────────────────────────────────────────────
  3                8
  5                32           ⚠️ ko'p
  10               1024         ❌ boshqarib bo'lmaydi

=== ✅ 2. Kompozitsiya ===

  Sinflar soni:
    ⭐ JAMI: 9 sinf

  Jonzot                             Tezlik   Zarar   Jon
  ──────────────────────────────────────────────────────────
  Ajdaho (uchish, suzish, ...)           50      30    500
  Quyon (yugurish, hujumyoq)             30       0     30

  ⭐ JAMI: 15 × 4 = 60 xil jonzot — 9 SINF bilan

=== 4. Yangi xususiyat qo'shish ===

  ⭐ QO'SHILDI: 2 sinf
  ⭐ O'ZGARDI:  0 qator mavjud kod
  ⭐ YANGI KOMBINATSIYALAR: 2^5 × 5 = 160

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

Misol 3 — Uzatish naqshlari

python
"""Kompozitsiyada metodlarni uzatish."""

from __future__ import annotations

import functools
from typing import Any, Callable

print("=== 1. Qo'lda uzatish (tavsiya) ===\n")


class Ombor:
    """Asos — barcha imkoniyatlar."""

    def __init__(self):
        self._d: dict[str, Any] = {}

    def saqla(self, k: str, v: Any) -> None:
        self._d[k] = v

    def olish(self, k: str) -> Any:
        return self._d[k]

    def ochir(self, k: str) -> bool:
        return self._d.pop(k, None) is not None

    def kalitlar(self) -> list[str]:
        return list(self._d)

    def tozala(self) -> None:
        self._d.clear()

    def __len__(self): return len(self._d)
    def __contains__(self, k): return k in self._d


class OqishOmbor:
    """⭐ FAQAT o'qish metodlari uzatiladi."""

    __slots__ = ("_ichki",)

    def __init__(self, ichki: Ombor):
        self._ichki = ichki

    # ⭐ Faqat kerakli metodlar
    def olish(self, k: str) -> Any:
        return self._ichki.olish(k)

    def kalitlar(self) -> list[str]:
        return self._ichki.kalitlar()

    def __len__(self): return len(self._ichki)
    def __contains__(self, k): return k in self._ichki

    def __repr__(self):
        return f"OqishOmbor({len(self)} element)"


o = Ombor()
o.saqla("a", 1)
o.saqla("b", 2)

oq = OqishOmbor(o)

print(f"  Ombor:      {sorted(o.kalitlar())}")
print(f"  OqishOmbor: {oq}\n")

print(f"  {'Amal':<24} {'Ombor':<12} {'OqishOmbor'}")
print("  " + "─" * 50)
for kod, f1, f2 in [
    ("olish('a')",      lambda: o.olish("a"),      lambda: oq.olish("a")),
    ("len()",           lambda: len(o),            lambda: len(oq)),
    ("'a' in x",        lambda: "a" in o,          lambda: "a" in oq),
    ("kalitlar()",      lambda: sorted(o.kalitlar()),
     lambda: sorted(oq.kalitlar())),
    ("saqla('c', 3)",   lambda: o.saqla("c", 3),   lambda: oq.saqla("c", 3)),
    ("ochir('a')",      lambda: o.ochir("a"),      lambda: oq.ochir("a")),
    ("tozala()",        lambda: None,              lambda: oq.tozala()),
]:
    try:
        r1 = repr(f1())
    except Exception as e:
        r1 = f"❌ {type(e).__name__}"
    try:
        r2 = repr(f2())
    except AttributeError:
        r2 = "✅ AttributeError"
    print(f"  {kod:<24} {r1:<12} {r2}")

print(f"""
  ⭐ AFZALLIKLARI:
     • Aniq — qaysi metodlar bor, ko'rinadi
     • IDE avtoto'ldirish ishlaydi
     • mypy tekshiradi
     • Yozish imkoniyati BUTUNLAY yo'q

  ⚠️ Kamchiligi: har metodni qo'lda yozish kerak
""")


print("=== ⚠️ 2. __getattr__ bilan avtomatik ===\n")


class AvtoOram:
    """⚠️ HAMMASINI uzatadi."""

    def __init__(self, ichki):
        self._ichki = ichki

    def __getattr__(self, nom):
        print(f"      [__getattr__({nom!r})]")
        return getattr(self._ichki, nom)


a = AvtoOram(o)
o.saqla("a", 1)

print(f"  a.olish('a'):")
print(f"    → {a.olish('a')}\n")

print(f"  a.saqla('z', 99):   ⚠️ ham uzatiladi")
a.saqla("z", 99)
print(f"    Ombor: {sorted(o.kalitlar())}\n")

print(f"  ⚠️ DUNDER METODLAR UZATILMAYDI:")
for kod, f in [
    ("len(a)",      lambda: len(a)),
    ("'a' in a",    lambda: "a" in a),
    ("a['a']",      lambda: a["a"]),
]:
    try:
        n = f"⚠️ {f()!r}"
    except TypeError as e:
        n = f"❌ TypeError: {str(e)[:40]}"
    print(f"    {kod:<16} {n}")

print(f"""
  ⭐ SABAB: dunder metodlar TUR da qidiriladi,
     nusxada emas (8.15-dars, maxsus metod qidiruvi):

       len(x)  →  type(x).__len__(x)
                  ⚠️ __getattr__ ga BORMAYDI

  ⚠️ BOSHQA MUAMMOLAR:
     • IDE va mypy bilmaydi
     • Xato nomlar ham uzatiladi (typo aniqlanmaydi)
     • Nosozlikni topish qiyin
     • Rekursiya xavfi (_ichki dan oldin)
""")


class XavfliOram:
    """⚠️ Rekursiya xavfi."""

    def __init__(self, ichki):
        self.ichki = ichki                      # ⚠️ __getattr__ dan oldin

    def __getattr__(self, nom):
        return getattr(self.ichki, nom)         # ⚠️ ichki yo'q bo'lsa...


class XavfsizOram:
    """✅ object.__setattr__ bilan."""

    def __init__(self, ichki):
        object.__setattr__(self, "_ichki", ichki)

    def __getattr__(self, nom):
        if nom.startswith("_"):
            raise AttributeError(nom)           # ⭐ rekursiya himoyasi
        return getattr(self._ichki, nom)


x = XavfsizOram(o)
print(f"  XavfsizOram:")
print(f"    x.olish('a') = {x.olish('a')}")
try:
    x._mavjud_emas
except AttributeError as e:
    print(f"    x._mavjud_emas → ✅ AttributeError: {e}")


print("\n\n=== ⭐ 3. Dinamik uzatuvchi ===\n")


def uzat(ichki_nom: str, *metodlar: str):
    """⭐ Sinf dekoratori — dunder larni ham qamraydi."""

    def uzatuvchi_yasa(nom: str):
        def uzatuvchi(self, *args, **kwargs):
            return getattr(getattr(self, ichki_nom), nom)(*args, **kwargs)
        uzatuvchi.__name__ = nom
        uzatuvchi.__qualname__ = nom
        uzatuvchi.__doc__ = f"{ichki_nom}.{nom} ga uzatiladi."
        return uzatuvchi

    def dekorator(cls):
        for nom in metodlar:
            setattr(cls, nom, uzatuvchi_yasa(nom))
        return cls

    return dekorator


@uzat("_ichki", "olish", "kalitlar", "__len__", "__contains__")
class DinamikOqish:
    """⭐ Uzatiladigan metodlar dekoratorda ko'rinadi."""

    __slots__ = ("_ichki",)

    def __init__(self, ichki):
        self._ichki = ichki

    def __repr__(self):
        return f"DinamikOqish({len(self)} element)"


d = DinamikOqish(o)

print(f"  {d}\n")
for kod, f in [
    ("d.olish('a')",    lambda: d.olish("a")),
    ("len(d)",          lambda: len(d)),
    ("'a' in d",        lambda: "a" in d),
    ("d.kalitlar()",    lambda: sorted(d.kalitlar())),
    ("d.saqla('x', 1)", lambda: d.saqla("x", 1)),
    ("d.ochir('a')",    lambda: d.ochir("a")),
]:
    try:
        n = f"✅ {f()!r}"
    except AttributeError:
        n = "✅ AttributeError (uzatilmagan)"
    print(f"    {kod:<20} {n}")

print(f"\n  Hujjat:")
print(f"    DinamikOqish.olish.__doc__ = "
      f"{DinamikOqish.olish.__doc__!r}")

print(f"""
  ⭐ AFZALLIKLARI:
     • Dunder metodlar ham uzatiladi
     • Ro'yxat DEKORATORDA ko'rinadi
     • Kod takrorlanmaydi

  ⚠️ Kamchiligi: mypy hali ham bilmaydi
""")


print("=== 4. Bezak (Decorator) naqshi ===\n")


class KeshlanganOmbor:
    """⭐ Mavjud omborga QATLAM qo'shadi."""

    def __init__(self, ichki: Ombor, maks: int = 100):
        self._ichki = ichki
        self._kesh: dict[str, Any] = {}
        self.maks = maks
        self.urish = 0
        self.otkazish = 0

    def olish(self, k: str) -> Any:
        if k in self._kesh:
            self.urish += 1
            return self._kesh[k]
        self.otkazish += 1
        v = self._ichki.olish(k)
        if len(self._kesh) >= self.maks:
            self._kesh.pop(next(iter(self._kesh)))
        self._kesh[k] = v
        return v

    def saqla(self, k: str, v: Any) -> None:
        self._ichki.saqla(k, v)
        self._kesh[k] = v                       # ⭐ keshni yangilaydi

    def ochir(self, k: str) -> bool:
        self._kesh.pop(k, None)
        return self._ichki.ochir(k)

    def kalitlar(self): return self._ichki.kalitlar()
    def __len__(self): return len(self._ichki)
    def __contains__(self, k): return k in self._ichki

    def statistika(self):
        jami = self.urish + self.otkazish
        return {"kesh": len(self._kesh), "urish": self.urish,
                "otkazish": self.otkazish,
                "nisbat": f"{self.urish / jami:.0%}" if jami else "—"}


class JurnalliOmbor:
    """⭐ Yana bir qatlam."""

    def __init__(self, ichki):
        self._ichki = ichki
        self.jurnal: list[str] = []

    def olish(self, k):
        self.jurnal.append(f"OLISH {k}")
        return self._ichki.olish(k)

    def saqla(self, k, v):
        self.jurnal.append(f"SAQLA {k}")
        self._ichki.saqla(k, v)

    def ochir(self, k):
        self.jurnal.append(f"OCHIR {k}")
        return self._ichki.ochir(k)

    def kalitlar(self): return self._ichki.kalitlar()
    def __len__(self): return len(self._ichki)
    def __contains__(self, k): return k in self._ichki


asos = Ombor()
asos.saqla("a", 1)
asos.saqla("b", 2)

# ⭐ Qatlamlarni ISTALGAN tartibda ulash
kesh = KeshlanganOmbor(asos)
toliq = JurnalliOmbor(kesh)

print(f"  Qatlamlar: JurnalliOmbor(KeshlanganOmbor(Ombor))\n")

for k in ["a", "b", "a", "a", "b"]:
    toliq.olish(k)

print(f"  5 ta o'qish (a, b, a, a, b):")
print(f"    Kesh: {kesh.statistika()}")
print(f"    Jurnal: {toliq.jurnal}")

print(f"\n  ⭐ Tartibni o'zgartirish:")
kesh2 = KeshlanganOmbor(JurnalliOmbor(asos))
for k in ["a", "a", "a"]:
    kesh2.olish(k)
print(f"    KeshlanganOmbor(JurnalliOmbor(Ombor)):")
print(f"      Jurnal: {kesh2._ichki.jurnal}   "
      f"⭐ faqat 1 marta (kesh to'sdi)")

print(f"""
  ⭐ BEZAK NAQSHI:
     • Har qatlam bir mas'uliyat
     • Istalgan tartibda ulanadi
     • Tartib xatti-harakatni o'zgartiradi
     • Meros bilan bu MUMKIN EMAS (sinf tartibi qotgan)
""")


print("=== 5. Strategiya naqshi ===\n")


class Saralovchi:
    """⭐ Algoritm — ish vaqtida almashtiriladi."""

    def __init__(self, algoritm: Callable[[list], list] | None = None):
        self.algoritm = algoritm or sorted
        self.chaqiruvlar = 0

    def sarala(self, m: list) -> list:
        self.chaqiruvlar += 1
        return self.algoritm(m)


def pufakcha(m: list) -> list:
    m = list(m)
    for i in range(len(m)):
        for j in range(len(m) - i - 1):
            if m[j] > m[j + 1]:
                m[j], m[j + 1] = m[j + 1], m[j]
    return m


def teskari(m: list) -> list:
    return sorted(m, reverse=True)


def uzunlik_boyicha(m: list) -> list:
    return sorted(m, key=lambda x: (len(str(x)), str(x)))


MALUMOT = [100, 5, 33, 2, 999, 7]

s = Saralovchi()
print(f"  Kirish: {MALUMOT}\n")

for nom, alg in [("sorted (sukut)", None), ("pufakcha", pufakcha),
                 ("teskari", teskari),
                 ("uzunlik bo'yicha", uzunlik_boyicha),
                 ("lambda", lambda m: sorted(m, key=lambda x: -x % 10))]:
    if alg:
        s.algoritm = alg                        # ⭐ ish vaqtida
    print(f"    {nom:<20} {s.sarala(MALUMOT)}")

print(f"\n    Chaqiruvlar: {s.chaqiruvlar}")

print(f"""
  ⭐ MEROS BILAN:
     class SaralovchiSorted(Saralovchi): ...
     class SaralovchiPufakcha(Saralovchi): ...
     class SaralovchiTeskari(Saralovchi): ...
     ⚠️ Har algoritm uchun sinf, ish vaqtida almashtirib bo'lmaydi

  ✅ KOMPOZITSIYA:
     • Bitta sinf
     • Funksiya ham, obyekt ham bo'lishi mumkin
     • Ish vaqtida almashtiriladi
     • Test qilish oson (soxta algoritm)
""")

Natijaning muhim qismi:

text
=== 1. Qo'lda uzatish (tavsiya) ===

  Amal                     Ombor        OqishOmbor
  ──────────────────────────────────────────────────
  olish('a')               1            1
  len()                    2            2
  saqla('c', 3)            None         ✅ AttributeError
  tozala()                 None         ✅ AttributeError

=== ⚠️ 2. __getattr__ bilan avtomatik ===

  ⚠️ DUNDER METODLAR UZATILMAYDI:
    len(a)           ❌ TypeError: object of type 'AvtoOram' has no len()
    a['a']           ❌ TypeError: 'AvtoOram' object is not subscriptable

=== 4. Bezak (Decorator) naqshi ===

  5 ta o'qish (a, b, a, a, b):
    Kesh: {'kesh': 2, 'urish': 3, 'otkazish': 2, 'nisbat': '60%'}

  ⭐ Tartibni o'zgartirish:
      Jurnal: ['OLISH a']   ⭐ faqat 1 marta (kesh to'sdi)

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

Misol 4 — Amaliy: hujjat tizimi (8-qism yakuni)

python
"""8-qismning barcha tushunchalari bir loyihada."""

from __future__ import annotations

import inspect
import json
import re
from abc import ABC, abstractmethod
from collections.abc import Sequence
from dataclasses import dataclass, field, replace
from datetime import datetime
from enum import Enum
from functools import cached_property
from typing import Any, ClassVar, Iterator, Protocol, runtime_checkable

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


# ═══ 1. ENUM 8.17-bob ═══

class Holat(str, Enum):
    QORALAMA = "qoralama"
    KORIB_CHIQISHDA = "korib_chiqishda"
    NASHR = "nashr"
    ARXIV = "arxiv"

    def __str__(self): return self.value


# ═══ 2. FROZEN DATACLASS (8.17, 8.19) ═══

@dataclass(frozen=True, slots=True, order=True)
class Versiya:
    """⭐ Meros: dataclass — is-a Versiya."""
    katta: int
    kichik: int = 0

    def keyingi(self, katta: bool = False) -> "Versiya":
        return (Versiya(self.katta + 1, 0) if katta
                else Versiya(self.katta, self.kichik + 1))

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


# ═══ 3. PROTOCOL (8.9, 8.18) ═══

@runtime_checkable
class Formatlagich(Protocol):
    """⭐ Tashqi sinflar meros olmasdan mos keladi."""
    def formatla(self, matn: str) -> str: ...


@runtime_checkable
class Tekshiruvchi(Protocol):
    def tekshir(self, hujjat: "Hujjat") -> list[str]: ...


# ═══ 4. ABC 8.18-bob ═══

class Eksportchi(ABC):
    """⭐ Meros: to'g'ri — har avlod haqiqiy eksportchi."""

    KENGAYTMA: ClassVar[str] = ".txt"
    MIME: ClassVar[str] = "text/plain"

    _royxat: ClassVar[dict[str, type["Eksportchi"]]] = {}

    def __init_subclass__(cls, **kw):
        super().__init_subclass__(**kw)
        # ⚠️ cls.__abstractmethods__ bu yerda hali YO'Q — ABCMeta uni
        #    __init_subclass__ ishlagandan KEYIN o'rnatadi. inspect.isabstract
        #    aynan shu holatda abstrakt metodlarni qo'lda tekshiradi.
        if not inspect.isabstract(cls):         # ⭐ faqat to'liq sinflar
            Eksportchi._royxat[cls.KENGAYTMA.lstrip(".")] = cls

    @abstractmethod
    def _tana(self, hujjat: "Hujjat") -> str:
        """Hujjat tanasini formatlaydi."""

    def eksport(self, hujjat: "Hujjat") -> str:
        """⭐ Shablon metod — tartib belgilangan."""
        qismlar = [self._sarlavha(hujjat), self._tana(hujjat)]
        ostki = self._ostki(hujjat)
        if ostki:
            qismlar.append(ostki)
        return "\n\n".join(q for q in qismlar if q)

    def _sarlavha(self, hujjat: "Hujjat") -> str:
        return hujjat.sarlavha

    def _ostki(self, hujjat: "Hujjat") -> str:
        return f"— {hujjat.muallif}, {hujjat.versiya}"

    @classmethod
    def yarat(cls, kengaytma: str) -> "Eksportchi":
        k = kengaytma.lstrip(".")
        if k not in cls._royxat:
            raise KeyError(
                f"Noma'lum format: {k!r}. Mavjud: {sorted(cls._royxat)}"
            )
        return cls._royxat[k]()

    @classmethod
    def mavjudlar(cls) -> list[str]:
        return sorted(cls._royxat)


class MatnEksportchi(Eksportchi):
    KENGAYTMA = ".txt"

    def _sarlavha(self, h):
        return f"{h.sarlavha}\n{'=' * len(h.sarlavha)}"

    def _tana(self, h): return h.matn


class MarkdownEksportchi(Eksportchi):
    KENGAYTMA = ".md"
    MIME = "text/markdown"

    def _sarlavha(self, h): return f"# {h.sarlavha}"

    def _tana(self, h):
        teglar = " ".join(f"`{t}`" for t in h.teglar)
        return f"{teglar}\n\n{h.matn}" if teglar else h.matn

    def _ostki(self, h):
        return f"---\n*{h.muallif}* · {h.versiya} · {h.holat}"


class JsonEksportchi(Eksportchi):
    KENGAYTMA = ".json"
    MIME = "application/json"

    def _sarlavha(self, h): return ""
    def _ostki(self, h): return ""

    def _tana(self, h):
        return json.dumps({
            "sarlavha": h.sarlavha,
            "matn": h.matn,
            "muallif": h.muallif,
            "versiya": str(h.versiya),
            "holat": str(h.holat),
            "teglar": sorted(h.teglar),
            "statistika": h.statistika,
        }, ensure_ascii=False, indent=2)


# ═══ 5. ASOSIY SINF (8.3, 8.10, 8.11, 8.15) ═══

class Hujjat:
    """Bitta hujjat.

    KOMPOZITSIYA:
      • formatlagichlar — ish vaqtida almashtiriladi
      • tekshiruvchilar — ro'yxatga qo'shiladi
      • eksportchi — kerak bo'lganda yaratiladi

    MEROS: yo'q — Hujjat hech narsaning avlodi emas.
    """

    MAKS_SARLAVHA: ClassVar[int] = 200

    def __init__(self, sarlavha: str, matn: str = "",
                 muallif: str = "", *,
                 formatlagichlar: list[Formatlagich] | None = None,
                 tekshiruvchilar: list[Tekshiruvchi] | None = None):
        self._sarlavha = ""
        self.sarlavha = sarlavha                # ⭐ setter tekshiradi
        self._matn = matn
        self.muallif = muallif
        self._versiya = Versiya(1, 0)
        self._holat = Holat.QORALAMA
        self._teglar: set[str] = set()
        self._tarix: list[tuple[datetime, str]] = [
            (datetime.now(), "yaratildi")
        ]
        # ⭐ KOMPOZITSIYA — ish vaqtida almashtiriladi
        self.formatlagichlar = list(formatlagichlar or [])
        self.tekshiruvchilar = list(tekshiruvchilar or [])

    # ── @property 8.15-bob ──

    @property
    def sarlavha(self) -> str:
        return self._sarlavha

    @sarlavha.setter
    def sarlavha(self, q: str) -> None:
        q = q.strip()
        if not q:
            raise ValueError("Sarlavha bo'sh bo'lmasin")
        if len(q) > self.MAKS_SARLAVHA:
            raise ValueError(f"Sarlavha {self.MAKS_SARLAVHA} belgidan uzun")
        self._sarlavha = q

    @property
    def matn(self) -> str:
        """⭐ Formatlagichlar QATLAMI qo'llanadi."""
        natija = self._matn
        for f in self.formatlagichlar:
            natija = f.formatla(natija)
        return natija

    @matn.setter
    def matn(self, q: str) -> None:
        self._matn = q
        self._versiya = self._versiya.keyingi()
        self._tarix.append((datetime.now(), "matn o'zgardi"))
        self.__dict__.pop("statistika", None)   # ⭐ keshni bekor qilish

    @property
    def xom_matn(self) -> str:
        return self._matn

    @property
    def versiya(self) -> Versiya: return self._versiya

    @property
    def holat(self) -> Holat: return self._holat

    @property
    def teglar(self) -> frozenset[str]:
        return frozenset(self._teglar)          # ⭐ himoyalangan

    @property
    def tarix(self) -> tuple[tuple[datetime, str], ...]:
        return tuple(self._tarix)

    @cached_property
    def statistika(self) -> dict[str, int]:
        """⭐ Qimmat hisoblash — keshlanadi."""
        sozlar = re.findall(r"[^\W\d_]+", self._matn.lower())
        return {
            "belgilar": len(self._matn),
            "sozlar": len(sozlar),
            "noyob": len(set(sozlar)),
            "qatorlar": self._matn.count("\n") + 1 if self._matn else 0,
        }

    # ── Amallar ──

    def teg_qosh(self, *teglar: str) -> "Hujjat":
        for t in teglar:
            t = re.sub(r"[^\w-]", "", t.strip().lower())
            if t:
                self._teglar.add(t)
        return self                             # ⭐ zanjir

    def holat_ozgartir(self, yangi: Holat) -> "Hujjat":
        OTISHLAR = {
            Holat.QORALAMA: {Holat.KORIB_CHIQISHDA, Holat.ARXIV},
            Holat.KORIB_CHIQISHDA: {Holat.NASHR, Holat.QORALAMA},
            Holat.NASHR: {Holat.ARXIV},
            Holat.ARXIV: {Holat.QORALAMA},
        }
        if yangi not in OTISHLAR[self._holat]:
            raise ValueError(
                f"{self._holat} → {yangi} mumkin emas. "
                f"Ruxsat: {sorted(str(h) for h in OTISHLAR[self._holat])}"
            )
        if yangi == Holat.NASHR:
            xatolar = self.tekshir()
            if xatolar:
                raise ValueError(f"Nashrdan oldin: {xatolar}")
        self._tarix.append((datetime.now(), f"{self._holat} → {yangi}"))
        self._holat = yangi
        return self

    def tekshir(self) -> list[str]:
        """⭐ Barcha tekshiruvchilarni ishga soladi."""
        xatolar = []
        for t in self.tekshiruvchilar:
            xatolar.extend(t.tekshir(self))
        return xatolar

    def eksport(self, format: str = "txt") -> str:
        return Eksportchi.yarat(format).eksport(self)

    # ── Dunder (8.11-8.14) ──

    def __repr__(self):
        return (f"Hujjat({self._sarlavha!r}, {self._versiya}, "
                f"{self._holat}, {len(self._teglar)} teg)")

    def __str__(self):
        return f"{self._sarlavha} ({self._versiya}, {self._holat})"

    def __len__(self): return len(self._matn)
    def __bool__(self): return bool(self._matn.strip())

    def __contains__(self, x):
        if isinstance(x, str):
            return x.lower() in self._matn.lower() or x.lower() in self._teglar
        return False

    def __eq__(self, b):
        if not isinstance(b, Hujjat):
            return NotImplemented
        return (self._sarlavha, self._matn) == (b._sarlavha, b._matn)

    def __hash__(self):
        return hash((self._sarlavha, self._matn))


# ═══ 6. KOMPOZITSIYA QISMLARI ═══

class Katta:
    """⭐ Protocol ga mos — meros YO'Q."""
    def formatla(self, matn: str) -> str:
        return matn.upper()


class Qisqartir:
    def __init__(self, maks: int = 100):
        self.maks = maks

    def formatla(self, matn: str) -> str:
        return (matn if len(matn) <= self.maks
                else matn[:self.maks - 3] + "...")


class Almashtir:
    def __init__(self, jadval: dict[str, str]):
        self.jadval = jadval

    def formatla(self, matn: str) -> str:
        for a, b in self.jadval.items():
            matn = matn.replace(a, b)
        return matn


class UzunlikTekshiruvchi:
    def __init__(self, eng_kam: int = 10, eng_kop: int = 100_000):
        self.eng_kam, self.eng_kop = eng_kam, eng_kop

    def tekshir(self, hujjat: Hujjat) -> list[str]:
        n = len(hujjat.xom_matn)
        if n < self.eng_kam:
            return [f"Matn juda qisqa: {n} < {self.eng_kam}"]
        if n > self.eng_kop:
            return [f"Matn juda uzun: {n} > {self.eng_kop}"]
        return []


class TegTekshiruvchi:
    def __init__(self, eng_kam: int = 1):
        self.eng_kam = eng_kam

    def tekshir(self, hujjat: Hujjat) -> list[str]:
        if len(hujjat.teglar) < self.eng_kam:
            return [f"Kamida {self.eng_kam} teg kerak"]
        return []


class MuallifTekshiruvchi:
    def tekshir(self, hujjat: Hujjat) -> list[str]:
        return [] if hujjat.muallif.strip() else ["Muallif ko'rsatilmagan"]


# ═══ 7. KONTEYNER 8.14-bob ═══

class Kutubxona(Sequence):
    """⭐ Sequence dan meros — is-a to'g'ri.

    Ichida list bor (kompozitsiya), lekin o'zi Sequence.
    """

    def __init__(self, hujjatlar=()):
        self._h: list[Hujjat] = list(hujjatlar)

    def __getitem__(self, i):
        if isinstance(i, slice):
            return Kutubxona(self._h[i])
        if isinstance(i, str):
            for h in self._h:
                if h.sarlavha == i:
                    return h
            raise KeyError(i)
        return self._h[i]

    def __len__(self): return len(self._h)

    def qosh(self, *hujjatlar: Hujjat) -> "Kutubxona":
        self._h.extend(hujjatlar)
        return self

    def filtrla(self, **shartlar) -> "Kutubxona":
        natija = self._h
        if "holat" in shartlar:
            natija = [h for h in natija if h.holat == shartlar["holat"]]
        if "teg" in shartlar:
            natija = [h for h in natija if shartlar["teg"] in h.teglar]
        if "muallif" in shartlar:
            natija = [h for h in natija if h.muallif == shartlar["muallif"]]
        return Kutubxona(natija)

    def __repr__(self):
        return f"Kutubxona({len(self)} hujjat)"


print("1. Hujjat yaratish (kompozitsiya bilan):\n")

h = Hujjat(
    "Python OOP",
    "Python da obyektga yo'naltirilgan dasturlash. "
    "Sinflar, meros va kompozitsiya.",
    "Aziz Karimov",
    tekshiruvchilar=[UzunlikTekshiruvchi(20), TegTekshiruvchi(2),
                     MuallifTekshiruvchi()],
)
h.teg_qosh("python", "OOP", "  Darslik  ")

print(f"  {h!r}")
print(f"    teglar: {sorted(h.teglar)}")
print(f"    statistika: {h.statistika}")
print(f"    len(h) = {len(h)}, bool(h) = {bool(h)}")
print(f"    'python' in h = {'python' in h}")


print("\n\n2. ⭐ Formatlagichlar (ish vaqtida):\n")

print(f"  Boshida: {h.matn[:50]}...\n")

h.formatlagichlar.append(Qisqartir(40))
print(f"  + Qisqartir(40):")
print(f"    {h.matn}")

h.formatlagichlar.append(Katta())
print(f"\n  + Katta():")
print(f"    {h.matn}")

h.formatlagichlar = [Almashtir({"Python": "Pythön", "OOP": "OYD"})]
print(f"\n  Almashtir bilan almashtirildi:")
print(f"    {h.matn[:60]}...")

h.formatlagichlar.clear()
print(f"\n  Tozalandi: {h.matn[:40]}...")

print(f"\n  ⭐ xom_matn hech qachon o'zgarmadi:")
print(f"    {h.xom_matn[:40]}...")


print("\n\n3. Tekshiruvchilar:\n")

print(f"  Tekshiruvchilar: "
      f"{[type(t).__name__ for t in h.tekshiruvchilar]}")
print(f"  Natija: {h.tekshir() or '✅ xato yo`q'}")

print(f"\n  Muammoli hujjat:")
h2 = Hujjat("Qisqa", "Kam", tekshiruvchilar=h.tekshiruvchilar)
for x in h2.tekshir():
    print(f"    ❌ {x}")

print(f"\n  Protocol tekshiruvi:")
for t in h.tekshiruvchilar:
    print(f"    isinstance({type(t).__name__}, Tekshiruvchi) = "
          f"{isinstance(t, Tekshiruvchi)}   ⭐ meros yo'q")


print("\n\n4. Holat mashinasi:\n")

print(f"  Joriy: {h.holat}")
for yangi in [Holat.KORIB_CHIQISHDA, Holat.NASHR, Holat.ARXIV]:
    try:
        h.holat_ozgartir(yangi)
        print(f"    → {yangi}   ✅")
    except ValueError as e:
        print(f"    → {yangi}   ❌ {e}")

print(f"\n  Noto'g'ri o'tish:")
try:
    h.holat_ozgartir(Holat.NASHR)
except ValueError as e:
    print(f"    {str(e)[:64]}")

print(f"\n  Tekshiruvsiz nashr:")
try:
    h2.holat_ozgartir(Holat.KORIB_CHIQISHDA).holat_ozgartir(Holat.NASHR)
except ValueError as e:
    print(f"    ❌ {str(e)[:70]}")


print("\n\n5. Eksport (ABC + shablon metod):\n")

print(f"  Mavjud formatlar: {Eksportchi.mavjudlar()}\n")

h.holat_ozgartir(Holat.QORALAMA)
for fmt in Eksportchi.mavjudlar():
    natija = h.eksport(fmt)
    print(f"  ┌─ {fmt}")
    for q in natija.split("\n")[:5]:
        print(f"  │ {q[:64]}")
    qolgan = len(natija.split("\n")) - 5
    if qolgan > 0:
        print(f"  │ ... yana {qolgan} qator")
    print(f"  └{'─' * 60}\n")


print("6. Kutubxona (Sequence merosi):\n")

k = Kutubxona()
k.qosh(
    h,
    Hujjat("Ma'lumot tuzilmalari", "Ro'yxat, lug'at va to'plamlar. " * 3,
           "Aziz Karimov").teg_qosh("python", "algoritm"),
    Hujjat("Veb dasturlash", "Flask va Django asoslari. " * 4,
           "Malika Yusupova").teg_qosh("python", "veb"),
    Hujjat("JavaScript", "Brauzerdagi dasturlash. " * 5,
           "Bobur Aliyev").teg_qosh("javascript", "veb"),
)

print(f"  {k!r}\n")
for x in k:
    print(f"    {x}")

print(f"\n  Sequence metodlari (bepul):")
print(f"    len(k)          = {len(k)}")
print(f"    k[0]            = {k[0]}")
print(f"    k[-1]           = {k[-1]}")
print(f"    k[1:3]          = {k[1:3]!r}")
print(f"    k['JavaScript'] = {k['JavaScript']}")
print(f"    h in k          = {h in k}")
print(f"    k.index(h)      = {k.index(h)}")

print(f"\n  Filtrlash:")
for shart in [{"teg": "python"}, {"teg": "veb"},
              {"muallif": "Aziz Karimov"},
              {"holat": Holat.QORALAMA}]:
    natija = k.filtrla(**shart)
    print(f"    {str(shart):<32} → "
          f"{[x.sarlavha for x in natija]}")


print("\n\n7. ⭐ Arxitektura qarorlari:\n")

QARORLAR = [
    ("Versiya", "@dataclass(frozen, slots, order)", "MEROS",
     "is-a: dataclass mexanizmi"),
    ("Holat", "Enum", "MEROS", "is-a: sanab o'tiladigan qiymat"),
    ("Eksportchi", "ABC + shablon metod", "MEROS",
     "is-a: har avlod haqiqiy eksportchi"),
    ("Kutubxona", "Sequence", "MEROS",
     "is-a: ketma-ketlik, HAR metod ma'noli"),
    ("Hujjat.formatlagichlar", "list[Protocol]", "KOMPOZITSIYA",
     "ish vaqtida almashtiriladi"),
    ("Hujjat.tekshiruvchilar", "list[Protocol]", "KOMPOZITSIYA",
     "mustaqil o'lcham"),
    ("Kutubxona._h", "list", "KOMPOZITSIYA",
     "list metodlarining yarmi ortiqcha"),
]

print(f"  {'Komponent':<28} {'Qaror':<16} {'Sabab'}")
print("  " + "─" * 76)
for nom, mexanizm, qaror, sabab in QARORLAR:
    belgi = "🔷" if qaror == "MEROS" else "🔶"
    print(f"  {belgi} {nom:<26} {qaror:<16} {sabab}")

print(f"""

  ⭐ 8-QISM XULOSASI — BU LOYIHADA:

     8.1-8.5   Sinf, __init__, atributlar, metodlar
     8.6-8.8   Meros (Eksportchi, Kutubxona), MRO
     8.9       Polimorfizm (Protocol, duck typing)
     8.10      Kapsulatsiya (_matn, __dict__ himoyasi)
     8.11-8.14 Dunder: __repr__, __eq__, __len__, __contains__
     8.15      @property, @cached_property (statistika)
     8.16      @classmethod (Eksportchi.yarat, mavjudlar)
     8.17      dataclass (Versiya), Enum (Holat)
     8.18      ABC (Eksportchi), __init_subclass__ registry
     8.19      __slots__ (Versiya)
     8.20      Kompozitsiya (formatlagichlar, tekshiruvchilar)

  ⭐ QAROR MEZONI:
     MEROS        — 4 joyda (barchasi haqiqiy "is-a")
     KOMPOZITSIYA — 3 joyda (almashtiriladigan xatti-harakat)
""")

Natijaning muhim qismi:

text
2. ⭐ Formatlagichlar (ish vaqtida):

  + Qisqartir(40):
    Python da obyektga yo'naltirilgan dast...

  + Katta():
    PYTHON DA OBYEKTGA YO'NALTIRILGAN DAS...

  ⭐ xom_matn hech qachon o'zgarmadi

3. Tekshiruvchilar:

  Muammoli hujjat:
    ❌ Matn juda qisqa: 3 < 20
    ❌ Kamida 2 teg kerak

  isinstance(UzunlikTekshiruvchi, Tekshiruvchi) = True   ⭐ meros yo'q

7. ⭐ Arxitektura qarorlari:

  🔷 Versiya                     MEROS            is-a: dataclass mexanizmi
  🔷 Eksportchi                 MEROS            is-a: har avlod haqiqiy eksportchi
  🔶 Hujjat.formatlagichlar      KOMPOZITSIYA     ish vaqtida almashtiriladi

Nima ko'rsatdi: 2.1, 2.2, 2.5, 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
"Kompozitsiya har doim yaxshi" Ortiqcha uzatish ham antipattern
"list/dict dan meros oddiy" Yarim metodlar ortiqcha bo'ladi
"UserList muammoni hal qiladi" Faqat C metodlari muammosini
"__getattr__ — yaxshi uzatish" Dunder metodlar uzatilmaydi
"Kompozitsiya sekinroq" Farq nanosekundlarda
"Meros yozish oson, demak yaxshi" Qisqa muddatli yutuq
"isinstance uchun meros kerak" Protocol yoki register()

6. Keng tarqalgan xatolar va yechimlari

1. O'rnatilgan turlardan meros

python
class Stack(list): ...              # ⚠️ 40 ortiqcha metod
class Stack:                        # ✅ kompozitsiya
    def __init__(self): self._el = []

2. "has-a" uchun meros

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

3. Kombinatsiya portlashi

python
class ZaharliUchuvchiSuzuvchi(...)  # ⚠️ 2ⁿ sinf
@dataclass
class Jonzot:
    xususiyatlar: list[Xususiyat]   # ✅

4. __getattr__ bilan to'liq uzatish

python
def __getattr__(self, n):
    return getattr(self._ichki, n)  # ⚠️ dunder uzatilmaydi
# ✅ qo'lda yoki dekorator bilan

5. Ortiqcha uzatish

python
class A:
    def m1(self): return self._b.m1()   # ⚠️ 20 ta shunday
    # ✅ Meros ko'rib chiqing yoki interfeysni qisqartiring

6. Ish vaqtida almashtirish kerak bo'lganda meros

python
class SaralovchiTez(Saralovchi): ...    # ⚠️
Saralovchi(algoritm=tez_saralash)       # ✅

7. super() chaqirilmaydigan mixin

python
class Mixin:
    def f(self): return "m"             # ⚠️ zanjir uziladi
    def f(self): return "m" + super().f()   # ✅

8. Chuqur ierarxiya

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

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 8.6-8.8-darslar (o'tilgan): meros, super(), MRO
  • 8.9, 8.18: Protocol, ABC — interfeys
  • 8.17: dataclass bilan kompozitsiya
  • 9-qism: istisnolar — meros to'g'ri bo'lgan misol
  • 10-qism: dekoratorlar — funksiyalar bilan kompozitsiya
  • Kitob: Design Patterns (GoF), Effective Python
  • Django, DRF, SQLAlchemy — ikkalasi ham keng ishlatiladi

8. Eng yaxshi amaliyotlar

  1. Sukut — kompozitsiya. Meros — asoslash talab qiladi.

  2. Uch savolni bering. "is-a", "has-a", "har metod ma'nolimi".

  3. O'rnatilgan turlardan meros olmang. UserDict/UserList yoki kompozitsiya.

  4. Mustaqil o'lchamlarni ajrating. Kombinatsiya portlashining oldini oladi.

  5. Uzatishni qo'lda yozing. __getattr__ — oxirgi chora.

  6. Meros faqat: ABC, framework, mixin, "is-a". Boshqa hollarda kompozitsiya.

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

  8. Protocol bilan interfeys. Meros talab qilmaydi.


9. Amaliy topshiriq

Vazifa 1: Meros yoki kompozitsiya?

1.  Stack va list
2.  ValidationError va Exception
3.  Mashina va Dvigatel
4.  Admin va Foydalanuvchi
5.  Kesh va dict
6.  Buyurtma va Mahsulot
7.  JsonEksportchi va Eksportchi(ABC)
8.  Xizmat va HttpMijoz
9.  Kvadrat va Togriturtburchak (o'zgaruvchan)
10. MeningTestim va unittest.TestCase
11. Saralovchi va saralash algoritmi
12. TekshirilganRoyxat va MutableSequence
Javoblar
  1. Kompozitsiya — list metodlarining yarmi ma'nosiz
  2. Meros — haqiqiy "is-a"
  3. Kompozitsiya — "mashinada dvigatel bor"
  4. Meros — "admin bu foydalanuvchi"
  5. Kompozitsiya yoki UserDict — kesh dict emas (TTL, chegara)
  6. Kompozitsiya — "buyurtmada mahsulotlar bor"
  7. Meros — ABC amalga oshirish
  8. Kompozitsiya — test uchun almashtirish kerak
  9. Ikkalasi ham emas — birodar sinflar (umumiy Shakl)
  10. Meros — framework kengaytirish nuqtasi
  11. Kompozitsiya — strategiya, ish vaqtida almashtiriladi
  12. Meros — har metod ma'noli, is-a to'g'ri

Vazifa 2: Xatolarni tuzating

python
1.  class Stack(list):
        def push(self, x): self.append(x)
2.  class Mashina(Dvigatel): ...
3.  class ZaharliUchuvchiSuzuvchi(Zaharli, Uchuvchi, Suzuvchi): ...
4.  class Oram:
        def __getattr__(self, n): return getattr(self._ichki, n)
    len(oram)
5.  class Sozlama(dict):
        def __setitem__(self, k, v): super().__setitem__(k.lower(), v)
6.  class SaralovchiTez(Saralovchi): ...
    class SaralovchiSekin(Saralovchi): ...
7.  class Mixin:
        def f(self): return "mixin"
8.  class A: ...
    class B(A): ...
    class C(B): ...
    class D(C): ...
    class E(D): ...
Javoblar
python
1.  Kompozitsiya: self._el = []
2.  self.dvigatel = Dvigatel()
3.  @dataclass class Jonzot: xususiyatlar: list[...]
4.  Dunder larni qo'lda uzating yoki dekorator ishlating
5.  class Sozlama(UserDict)
6.  Saralovchi(algoritm=...)  — strategiya
7.  return "mixin " + super().f()
8.  Ierarxiyani yassilang + kompozitsiya

Vazifa 3: Savat sinfi

Yozing:

  1. Kompozitsiya bilan (ichida list)
  2. Faqat kerakli metodlar: qosh, ochir, jami, tozala
  3. __len__, __iter__, __contains__, __bool__
  4. Chegirma strategiyasi (ish vaqtida almashtiriladi)
  5. list dan meros olgan versiya bilan solishtiring
  6. Qaysi biri qaysi holatda yaxshi — asoslang

Vazifa 4: Kombinatsiya portlashi

  1. 4 xususiyatli meros ierarxiyasini yozing (16 sinf)
  2. Uni kompozitsiyaga o'giring (6-8 sinf)
  3. Beshinchi xususiyat qo'shing — ikkalasida ham
  4. Kod qatorlarini solishtiring
  5. Ish vaqtida o'zgartirish imkoniyatini ko'rsating
  6. Yangi kombinatsiya qo'shish narxini o'lchang

Vazifa 5: Uzatish generatori

Vosita yozing:

  1. Sinf va uzatiladigan metodlar ro'yxatini olsin
  2. Dunder metodlarni ham qo'llab-quvvatlasin
  3. Imzoni saqlasin (functools.wraps)
  4. Tur ko'rsatkichlarini ko'chirsin
  5. Hujjat yaratsin
  6. __getattr__ bilan solishtiring

Vazifa 6: Refaktoring

Meros ierarxiyasini kompozitsiyaga o'giring:

  1. Mavjud kodni tahlil qiling
  2. Mustaqil o'lchamlarni aniqlang
  3. Interfeyslarni (Protocol) ajrating
  4. Bosqichma-bosqich o'tkazing
  5. Testlar buzilmasin
  6. Kod hajmi va moslashuvchanlikni solishtiring

Vazifa 7: O'ylash

Nega "Design Patterns" kitobidagi 23 naqshning ko'pchiligi Python kodida ko'rinmaydi?

Javob

Chunki ular tilning bir qismiga aylangan — naqsh nomiga ehtiyoj qolmagan.

1. Naqsh nima

Dizayn naqshi — tilning cheklovini aylanib o'tish usuli:

"Bu til X ni to'g'ridan-to'g'ri qo'llab-quvvatlamaydi, shuning uchun biz Y qilamiz."

Agar til X ni qo'llab-quvvatlasa — naqsh kerak emas.

2. Yo'qolgan naqshlar

Naqsh (GoF) Java da Pythonda
Factory Method Abstract class + subclass @classmethod
Strategy Interface + implementations Funksiya obyekti
Command Interface + execute() Funksiya yoki functools.partial
Iterator Interface + hasNext/next __iter__/yield
Decorator Wrapper class ierarxiyasi @dekorator
Singleton Private constructor + static Modul
Template Method Abstract class Bor, lekin soddaroq
Adapter Wrapper class Bor
Observer Interface + list Callback ro'yxati
Prototype clone() metodi copy.copy
Flyweight Factory + pool functools.lru_cache
Visitor Double dispatch singledispatch

3. Misollar

Strategy:

java
// Java — naqsh kerak
interface Saralash { List sarala(List m); }
class TezSaralash implements Saralash { ... }
class SekinSaralash implements Saralash { ... }

class Saralovchi {
    private Saralash strategiya;
    Saralovchi(Saralash s) { this.strategiya = s; }
}
python
# Python — naqsh KO'RINMAYDI
def saralovchi(m, algoritm=sorted):
    return algoritm(m)

saralovchi(m, tez_saralash)         # ⭐ funksiya — birinchi darajali

Strategy naqshi Pythonda funksiyani uzatish bo'lib qoldi.

Command:

java
interface Buyruq { void bajar(); }
class SaqlashBuyrugi implements Buyruq {
    public void bajar() { ... }
}
List<Buyruq> tarix = new ArrayList<>();
python
from functools import partial

tarix = []
tarix.append(partial(saqla, hujjat))    # ⭐ shunchaki funksiya
for buyruq in tarix:
    buyruq()

Singleton:

java
class Sozlama {
    private static Sozlama nusxa;
    private Sozlama() {}
    public static Sozlama olish() {
        if (nusxa == null) nusxa = new Sozlama();
        return nusxa;
    }
}
python
# sozlama.py — ⭐ modul allaqachon singleton
HOST = "localhost"
PORT = 8000

# ishlatish:
import sozlama
sozlama.HOST

Iterator:

java
class MeningRoyxatim implements Iterable<Integer> {
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            public boolean hasNext() { ... }
            public Integer next() { ... }
        };
    }
}
python
class MeningRoyxatim:
    def __iter__(self):
        yield from self._el             # ⭐ bitta qator

Decorator:

java
abstract class Ombor { abstract String olish(String k); }
class KeshlanganOmbor extends Ombor {
    private Ombor ichki;
    String olish(String k) { ... }
}
python
@lru_cache                              # ⭐ tilda bor
def olish(k): ...

# yoki obyekt uchun:
@cached_property
def qiymat(self): ...

4. Qolgan naqshlar

Ba'zi naqshlar hali ham kerak:

python
# ✅ Template Method — ABC bilan
class Quvur(ABC):
    def bajar(self, m):                 # ⭐ algoritm
        m = self._yukla(m)
        m = self._ishla(m)              # ⭐ ilgak
        return self._saqla(m)

    @abstractmethod
    def _ishla(self, m): ...

# ✅ Adapter — interfeys mos kelmasa
class EskiAdapter(YangiInterfeys):
    def __init__(self, eski): self._eski = eski
    def yangi_metod(self): return self._eski.eski_metod()

# ✅ Facade — murakkablikni yashirish
class Kutubxona:
    def kitob_top(self, isbn):
        # ichkarida: db, kesh, jurnal, tarmoq
        ...

# ✅ Composite — daraxt tuzilmalari
class Papka:
    def __init__(self): self.bolalar = []

# ✅ Observer — hodisa tizimlari
# (lekin Pythonda oddiy callback ro'yxati)

Naqsh: tuzilmaviy (structural) va xatti-harakat naqshlari qoladi; yaratuvchi (creational) naqshlar yo'qoladi.

5. Peter Norvig ning tahlili

1996 yilda Peter Norvig ("Design Patterns in Dynamic Languages") tahlil qildi:

"16 of the 23 patterns are either invisible or simpler in a dynamic language."

23 dan 16 tasi dinamik tilda ko'rinmaydi yoki soddalashadi.

Sabablari:

  1. Birinchi darajali funksiyalar — Strategy, Command, Visitor
  2. Birinchi darajali turlar — Factory, Abstract Factory
  3. Modullar — Singleton, Facade
  4. Dinamik atributlar — Prototype, Flyweight
  5. Ko'p meros / mixin — Decorator, Adapter

6. Nima uchun bu muhim

Java tajribasi bilan kelgan dasturchi Pythonda naqshlarni ko'chirib yozadi:

python
# ⚠️ Java uslubidagi Python
class AbstractSaralashStrategiyaFactory(ABC):
    @abstractmethod
    def yarat_strategiya(self) -> AbstractSaralashStrategiya: ...


class TezSaralashStrategiyaFactory(AbstractSaralashStrategiyaFactory):
    def yarat_strategiya(self):
        return TezSaralashStrategiya()


# ✅ Pythoncha
def sarala(m, key=None, reverse=False):
    return sorted(m, key=key, reverse=reverse)

Belgilar:

  • AbstractXFactory sinflari
  • IOmbor, IFoydalanuvchi (interfeys prefiksi)
  • Getter/setter
  • Har narsa uchun sinf
  • Chuqur ierarxiya

7. Pythonning "naqshlari"

Pythonda o'z idiomalari bor:

Idioma Nima hal qiladi
Duck typing Interfeys
Dekorator Cross-cutting concerns
Kontekst menejeri (with) Resurs boshqaruvi
Generator Dangasa hisoblash, Iterator
functools.partial Command, Currying
singledispatch Visitor, ortiqcha yuklash
Modul Singleton, Facade
dataclass Value Object, Builder
__init_subclass__ Registry
Protocol Interface

8. Amaliy tavsiya

python
# ⭐ 1. Avval eng ODDIY yechimni sinang
def ishla(m, f=sorted): return f(m)

# ⭐ 2. Yetmasa — funksiya obyekti
class Saralovchi:
    def __call__(self, m): ...

# ⭐ 3. Yetmasa — Protocol
class Algoritm(Protocol):
    def __call__(self, m: list) -> list: ...

# ⭐ 4. Yetmasa — ABC (umumiy kod kerak bo'lsa)
class AsosAlgoritm(ABC):
    @abstractmethod
    def _sarala(self, m): ...
    def sarala(self, m):        # umumiy tekshiruv
        ...

# ⚠️ 5. Faqat oxirida — to'liq naqsh

9. Xulosa

GoF naqshlari Pythonda ko'rinmaydi, chunki:

  1. Funksiyalar — birinchi darajali → Strategy, Command, Visitor yo'qoldi
  2. Sinflar — obyekt → Factory Method @classmethod bo'ldi
  3. Modullar → Singleton, Facade
  4. Dinamiklik → Prototype, Flyweight
  5. Dekoratorlar → Decorator naqshi tilga kirdi

Umumiy dars: naqsh — tilning cheklovi belgisi. Kodingizda ko'p naqsh nomi bo'lsa, ehtimol siz tilning imkoniyatlaridan to'liq foydalanmayapsiz.

Paul Graham:

"When you notice a pattern in your code, that's a sign that the language is not powerful enough."

Lekin teskarisi ham to'g'ri emas — naqshlarni butunlay rad etish ham xato. Template Method, Adapter, Facade, Composite — Pythonda ham foydali va o'z nomi bilan ishlatiladi.

Nimani mustahkamlaydi: 2.2, 2.5, 2.7-bo'limlar.


Xulosa

Bu darsda meros va kompozitsiya orasidagi tanlovni o'rgandik.

Eng muhim uch fikr:

  1. Uchinchi savol eng amaliy: "ota-sinfning HAR metodi ma'nolimi?" "is-a" testi ba'zan aldaydi — Stack va list munosabati mantiqan tuyulishi mumkin, lekin list.sort(), list.insert(), list.__setitem__() stack uchun ma'nosiz. Javob "yo'q" bo'lsa — kompozitsiya, hatto "is-a" to'g'ri ko'rinsa ham.

  2. Kompozitsiya kombinatsiya portlashini yo'q qiladi. Meros bilan n mustaqil xususiyat 2ⁿ sinf talab qiladi; kompozitsiya bilan — n sinf va istalgancha obyekt. Bundan tashqari, xususiyatni ish vaqtida almashtirish mumkin bo'ladi — meros bilan bu imkonsiz.

  3. Meros beshta holatda to'g'ri: haqiqiy "is-a", ABC/Protocol, framework kengaytirish nuqtasi, mixin va til konstruktsiyalari (Enum, dataclass, NamedTuple). Boshqa hollarda sukut tanlov — kompozitsiya. Uzatishni qo'lda yozing: __getattr__ dunder metodlarni uzatmaydi va IDE bilan mypy ni ko'r qiladi.


8-QISM YAKUNI

Yigirma darsda Pythonning obyekt modelini to'liq ko'rib chiqdik:

Darslar Mavzu Asosiy fikr
1-5 Asoslar class — bajariladigan kod; o'qish zanjir bo'ylab, yozish nusxaga
6-8 Meros "is-a", LSP, super() — MRO da keyingi, C3 linearizatsiyasi
9-10 Polimorfizm, kapsulatsiya Duck typing merosni talab qilmaydi; _ va __ — kelishuv
11-14 Dunder metodlar __repr__ asos; __eq__+__hash__ shartnomasi; NotImplemented
15-16 Deskriptorlar property — data deskriptor; cls() fabrikada
17-19 Zamonaviy vositalar dataclass sukut tuzog'ini taqiqlaydi; ABC — sukut kod; __slots__ — o'lchagandan keyin
20 Arxitektura Kompozitsiya — sukut, meros — asoslash bilan

Uch umumiy tamoyil:

  1. Pythonda hamma narsa protokol orqali ishlaydi. for — __iter__, + — __add__, obj.x — deskriptor, with — __enter__. Siz istalgan protokolga o'z turingizni qo'sha olasiz — bu duck typing ning texnik asosi.

  2. Har vosita — ixtiyoriy qatlam. Duck typing → tur ko'rsatkichi → Protocol + mypy → ABC + isinstance → pydantic. Xotira uchun: oddiy sinf → dataclass → slots=True → NamedTuple. Siz kerakli darajani tanlaysiz va faqat unga to'laysiz.

  3. Aniqlik moslashuvchanlikdan ustun. self yashirin emas, __hash__ avtomatik yaratilmaydi, NotImplemented istisno emas, __slots__ sukut bo'yicha yoqilmagan. Har safar Python jimgina xato o'rniga aniq xatti-harakatni tanlagan.

Keyingi qism — 9-qism: Xatolar va istisnolar. U yerda try/except/else/finally, istisnolar ierarxiyasi, o'z istisno turlaringiz va xatolarni to'g'ri qayta ishlash strategiyalarini o'rganamiz.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.20-dars: Kompozitsiya vs meros — IlmHamroh