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

8.14-dars: Dunder: konteyner protokoli

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


1. Kirish va motivatsiya

Bitta metod — va sinf for bilan ishlaydi:

python
class Sanoq:
    def __getitem__(self, i):
        if i >= 5:
            raise IndexError
        return i * 10


for x in Sanoq():               # ✅ ishlaydi!
    print(x)                    # 0 10 20 30 40

list(Sanoq())                   # [0, 10, 20, 30, 40]
20 in Sanoq()                   # True

__iter__ yozilmagan, lekin for ishladi. Nega?

Va bu — chalkash:

python
class A:
    def __len__(self): return 0

bool(A())                       # False  ⚠️ __bool__ yozilmagan
if A(): ...                     # bajarilmaydi

Va bu:

python
class B:
    def __contains__(self, x): return True

"har narsa" in B()              # True
for x in B(): ...               # ❌ TypeError

Bu darsda:

  • Konteyner protokollari: Sized, Iterable, Container, Sequence, Mapping
  • __getitem__ va eski iteratsiya protokoli
  • __iter__, __next__, __reversed__
  • Kesish (slice) va salbiy indekslar
  • __setitem__, __delitem__, __missing__
  • collections.abc va UserDict/UserList

2. Nazariya — chuqur tushuntirish

2.1. Protokollar iyerarxiyasi

Protokol Metodlar Beradi
Container __contains__ in
Sized __len__ len()
Iterable __iter__ for, list()
Iterator __iter__, __next__ next()
Reversible __reversed__ reversed()
Collection Sized + Iterable + Container uchalasi
Sequence __getitem__, __len__ [i], kesish, index, count
MutableSequence + __setitem__, __delitem__, insert append, pop, remove
Mapping __getitem__, __len__, __iter__ keys, values, items, get
MutableMapping + __setitem__, __delitem__ pop, update, setdefault
Set __contains__, __len__, __iter__ &, |, -, ^

collections.abc dan meros olsangiz — qolgan metodlar bepul.

2.2. Eski iteratsiya protokoli

python
class A:
    def __getitem__(self, i):
        if i >= 3: raise IndexError
        return i

for x in A(): ...               # ✅ ishlaydi

Mexanizm (iter(x)):

1. type(x).__iter__     bor bo'lsa → chaqiriladi
2. type(x).__getitem__  bor bo'lsa → ⭐ eski protokol:
     obj[0], obj[1], obj[2], ...  IndexError gacha
3. TypeError: 'A' object is not iterable

Bu — Python 2.0 dan qolgan orqaga moslik. Yangi kodda __iter__ yozing.

Tuzoq:

python
class Lugat:
    def __getitem__(self, kalit):
        return self._d[kalit]

for x in Lugat(): ...           # ⚠️ Lugat()[0] chaqiriladi → KeyError

KeyError — IndexError emas, shuning uchun tsikl to'xtamaydi.

2.3. __iter__ va __next__

python
class Diapazon:
    def __init__(self, n): self.n = n

    def __iter__(self):
        """⭐ YANGI iterator qaytaradi."""
        return DiapazonIter(self.n)


class DiapazonIter:
    def __init__(self, n):
        self.n, self.i = n, 0

    def __iter__(self):
        return self                     # ⭐ iterator o'zini qaytaradi

    def __next__(self):
        if self.i >= self.n:
            raise StopIteration         # ⭐ tugadi
        q = self.i
        self.i += 1
        return q

Iterable vs Iterator:

Iterable Iterator
Metodlar __iter__ __iter__ + __next__
Ko'p marta Har for yangi Bir marta
Misol list, dict, range iter(lst), generator

O'zi iterator bo'lgan sinf — bir martalik:

python
class Yomon:
    def __init__(self, n): self.n, self.i = n, 0
    def __iter__(self): return self         # ⚠️ o'zini
    def __next__(self):
        if self.i >= self.n: raise StopIteration
        self.i += 1
        return self.i - 1


y = Yomon(3)
list(y)                         # [0, 1, 2]
list(y)                         # []       ⚠️ tugagan

Generator bilan eng oson:

python
class Yaxshi:
    def __init__(self, n): self.n = n
    def __iter__(self):
        for i in range(self.n):
            yield i                     # ⭐ har safar yangi generator

2.4. __getitem__ va kesish

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

    def __getitem__(self, i):
        if isinstance(i, slice):        # ⭐ kesish
            return Royxat(self._el[i])
        if isinstance(i, int):
            if i < 0:
                i += len(self._el)      # ⭐ salbiy indeks
            if not 0 <= i < len(self._el):
                raise IndexError("indeks chegaradan tashqarida")
            return self._el[i]
        raise TypeError(
            f"indeks int yoki slice bo'lsin: {type(i).__name__}"
        )

Uch holat:

python
r[0]                            # int
r[1:3]                          # slice(1, 3, None)
r[::2]                          # slice(None, None, 2)
r["a"]                          # ⚠️ TypeError berish kerak

slice obyekti:

python
s = slice(1, 10, 2)
s.start, s.stop, s.step         # 1, 10, 2
s.indices(5)                    # (1, 5, 2)  ⭐ uzunlikka moslashtiradi

slice.indices(len) — salbiy va chegaradan tashqari qiymatlarni to'g'rilaydi.

Kesish natijasi — o'sha tur:

python
"abc"[0:2]                      # 'ab'      str
[1,2,3][0:2]                    # [1, 2]    list
(1,2,3)[0:2]                    # (1, 2)    tuple

Sizning sinfingiz ham shunday qilsin.

2.5. __contains__, __len__, __bool__

in mexanizmi:

x in obj:
  1. obj.__contains__(x)      bor bo'lsa
  2. iteratsiya:              obj bo'ylab yurib == bilan solishtiradi
  3. TypeError
python
class A:
    def __getitem__(self, i):
        if i >= 3: raise IndexError
        return i

2 in A()                        # True   ⭐ iteratsiya orqali

bool mexanizmi (8.13-darsda ko'rgan edik):

1. __bool__()
2. __len__() != 0
3. True

Tuzoq:

python
class Sozlama:
    def __len__(self): return len(self._d)

s = Sozlama()                   # bo'sh
if s:                           # ⚠️ False — kutilmagan bo'lishi mumkin
    ...
if s is not None:               # ✅ aniq
    ...

2.6. O'zgartiruvchi metodlar

python
class Konteyner:
    def __setitem__(self, k, v): ...        # obj[k] = v
    def __delitem__(self, k): ...           # del obj[k]
    def __missing__(self, k): ...           # ⭐ faqat dict avlodlarida

__missing__ — faqat dict dan meros olganda:

python
class Sukutli(dict):
    def __missing__(self, k):
        return f"<{k} yo'q>"

d = Sukutli(a=1)
d["a"]                          # 1
d["yoq"]                        # '<yoq yo'q>'
d.get("yoq")                    # None   ⚠️ get __missing__ ni chaqirmaydi

Oddiy sinfda __missing__ ishlamaydi — u dict.__getitem__ ichida chaqiriladi.

2.7. collections.abc va User*

A) ABC dan meros — metodlar bepul:

python
from collections.abc import Sequence

class Royxat(Sequence):
    def __init__(self, el): self._el = list(el)
    def __getitem__(self, i): return self._el[i]
    def __len__(self): return len(self._el)

# ⭐ BEPUL: __contains__, __iter__, __reversed__, index, count
ABC Yozish kerak Bepul
Sequence __getitem__, __len__ __contains__, __iter__, __reversed__, index, count
MutableSequence + __setitem__, __delitem__, insert append, extend, pop, remove, __iadd__, reverse
Mapping __getitem__, __len__, __iter__ __contains__, keys, items, values, get, __eq__
MutableMapping + __setitem__, __delitem__ pop, popitem, clear, update, setdefault
Set __contains__, __len__, __iter__ __le__, __lt__, __and__, __or__, __sub__, isdisjoint

B) UserDict, UserList, UserString:

python
from collections import UserDict

class Sozlama(UserDict):
    def __setitem__(self, k, v):
        super().__setitem__(k.lower(), v)       # ⭐ normallashtiradi

Nega dict dan meros olmaslik kerak:

python
class YomonSozlama(dict):
    def __setitem__(self, k, v):
        super().__setitem__(k.lower(), v)

d = YomonSozlama(A=1)           # ⚠️ __init__ __setitem__ ni chaqirmaydi
d                               # {'A': 1}  ⚠️ normallashmadi
d.update(B=2)                   # ⚠️ update ham chaqirmaydi
d                               # {'A': 1, 'B': 2}

C da yozilgan dict metodlari ichkarida __setitem__ ni chaqirmaydi.

UserDict — sof Python, self.data lug'atini o'raydi:

python
class Sozlama(UserDict):
    def __setitem__(self, k, v):
        super().__setitem__(k.lower(), v)

s = Sozlama(A=1)
s                               # {'a': 1}  ✅
s.update(B=2)
s                               # {'a': 1, 'b': 2}  ✅

3. Tez ma'lumotnoma

Protokollar

python
__len__          len(x), bool(x) (fallback)
__getitem__      x[i], eski iteratsiya, in (fallback)
__setitem__      x[i] = v
__delitem__      del x[i]
__contains__     x in obj
__iter__         for, list(), iter()
__next__         next()   (iterator)
__reversed__     reversed()
__missing__      ⭐ faqat dict avlodlarida

iter(x) tartibi

1. __iter__       bor bo'lsa
2. __getitem__    eski protokol: x[0], x[1], ... IndexError gacha
3. TypeError

in tartibi

1. __contains__
2. iteratsiya + ==
3. TypeError

Kesish

python
def __getitem__(self, i):
    if isinstance(i, slice):
        return type(self)(self._el[i])      # ⭐ o'sha tur
    if isinstance(i, int):
        if i < 0: i += len(self)            # salbiy
        if not 0 <= i < len(self):
            raise IndexError
        return self._el[i]
    raise TypeError

collections.abc

python
Sequence            → __getitem__, __len__
MutableSequence     → + __setitem__, __delitem__, insert
Mapping             → __getitem__, __len__, __iter__
MutableMapping      → + __setitem__, __delitem__
Set                 → __contains__, __len__, __iter__

UserDict/UserList   ⭐ dict/list dan meros o'rniga

4. Batafsil misollar

Misol 1 — Iteratsiya protokollari

python
"""__iter__, __next__, __getitem__ va ular orasidagi farq."""

from collections.abc import Iterable, Iterator, Sequence, Container, Sized

print("=== ⭐ 1. Eski iteratsiya protokoli ===\n")


class FaqatGetitem:
    """__iter__ YO'Q, lekin for ishlaydi."""

    def __init__(self, n): self.n = n

    def __getitem__(self, i):
        print(f"      __getitem__({i})")
        if i >= self.n:
            raise IndexError                # ⭐ tsiklni to'xtatadi
        return i * 10


print(f"  for x in FaqatGetitem(3):")
for x in FaqatGetitem(3):
    print(f"    → {x}")

print(f"\n  list(FaqatGetitem(3)) = ", end="")
print(list(FaqatGetitem(3)))

print(f"\n  10 in FaqatGetitem(3) = {10 in FaqatGetitem(3)}")
print(f"  99 in FaqatGetitem(3) = {99 in FaqatGetitem(3)}")

f = FaqatGetitem(3)
print(f"\n  isinstance(f, Iterable) = {isinstance(f, Iterable)}   "
      f"⚠️ False, lekin iteratsiya ishlaydi!")

print(f"""
  ⭐ iter(x) MEXANIZMI:
     1. type(x).__iter__      bor bo'lsa → chaqiriladi
     2. type(x).__getitem__   bor bo'lsa → ESKI PROTOKOL:
          x[0], x[1], x[2], ...  IndexError gacha
     3. TypeError

  ⚠️ collections.abc.Iterable FAQAT __iter__ ni tekshiradi.
""")


print("=== ⚠️ 2. Eski protokol tuzog'i ===\n")


class LugatKabi:
    """⚠️ Kalitlar satr — eski protokol buziladi."""

    def __init__(self): self._d = {"a": 1, "b": 2}

    def __getitem__(self, k):
        return self._d[k]                   # ⚠️ KeyError, IndexError emas


print(f"  for x in LugatKabi():")
try:
    for x in LugatKabi():
        print(f"    {x}")
        break
except KeyError as e:
    print(f"    ❌ KeyError: {e}")
    print(f"    ⚠️ x[0] chaqirildi → KeyError → tsikl TO'XTAMADI")

print(f"""
  ✅ YECHIM: __iter__ ni ANIQ yozing.

     class LugatKabi:
         def __iter__(self):
             return iter(self._d)
""")


print("=== 3. Iterable vs Iterator ===\n")


class OziIterator:
    """⚠️ O'zi iterator — BIR MARTALIK."""

    def __init__(self, n): self.n, self.i = n, 0
    def __iter__(self): return self                 # ⚠️ o'zini
    def __next__(self):
        if self.i >= self.n: raise StopIteration
        self.i += 1
        return self.i - 1


class AlohidaIterator:
    """✅ Har safar yangi iterator."""

    def __init__(self, n): self.n = n
    def __iter__(self): return _Iter(self.n)


class _Iter:
    def __init__(self, n): self.n, self.i = n, 0
    def __iter__(self): return self
    def __next__(self):
        if self.i >= self.n: raise StopIteration
        self.i += 1
        return self.i - 1


class Generatorli:
    """✅ Eng oson — generator."""

    def __init__(self, n): self.n = n
    def __iter__(self):
        for i in range(self.n):
            yield i


print(f"  {'Sinf':<20} {'1-marta':<14} {'2-marta':<14} {'Ichma-ich'}")
print("  " + "─" * 62)
for Sinf in (OziIterator, AlohidaIterator, Generatorli):
    x = Sinf(3)
    b1 = list(x)
    b2 = list(x)
    ichma = [(a, b) for a in x for b in x][:2]
    print(f"  {Sinf.__name__:<20} {str(b1):<14} {str(b2):<14} {ichma}")

print(f"""
  ⚠️ OziIterator: ikkinchi marta BO'SH, ichma-ich tsikl ishlamaydi

  ⭐ QOIDA:
     Iterable  — __iter__ YANGI iterator qaytaradi (ko'p marta)
     Iterator  — __iter__ o'zini qaytaradi + __next__ (bir marta)

     Sizning KONTEYNERINGIZ — Iterable bo'lsin.
""")

x = Generatorli(3)
print(f"  isinstance(Generatorli(3), Iterable) = {isinstance(x, Iterable)}")
print(f"  isinstance(Generatorli(3), Iterator) = {isinstance(x, Iterator)}")
print(f"  isinstance(iter(x), Iterator)        = {isinstance(iter(x), Iterator)}")


print("\n=== 4. Protokol tekshiruvi ===\n")


class Bosh: pass


class FaqatLen:
    def __len__(self): return 5


class FaqatContains:
    def __contains__(self, x): return x == 1


class FaqatIter:
    def __iter__(self): return iter([1, 2, 3])


class Toliq:
    def __len__(self): return 3
    def __iter__(self): return iter([1, 2, 3])
    def __contains__(self, x): return x in (1, 2, 3)
    def __getitem__(self, i): return [1, 2, 3][i]


SINFLAR = [Bosh, FaqatLen, FaqatContains, FaqatIter, Toliq,
           FaqatGetitem]

print(f"  {'Sinf':<18} {'len()':<8} {'in':<8} {'for':<8} {'[i]':<8} "
      f"{'Sized':<8} {'Iterable'}")
print("  " + "─" * 72)
for S in SINFLAR:
    x = S(3) if S is FaqatGetitem else S()
    natijalar = []
    for kod in ["len(x)", "1 in x", "list(x)", "x[0]"]:
        try:
            eval(kod, {"x": x})
            natijalar.append("✅")
        except (TypeError, IndexError, KeyError):
            natijalar.append("❌")
    sized = "✅" if isinstance(x, Sized) else "❌"
    iterable = "✅" if isinstance(x, Iterable) else "❌"
    print(f"  {S.__name__:<18} {natijalar[0]:<8} {natijalar[1]:<8} "
          f"{natijalar[2]:<8} {natijalar[3]:<8} {sized:<8} {iterable}")

print(f"""
  ⭐ KUZATUVLAR:
     • FaqatContains: `in` ishlaydi, `for` ishlamaydi
     • FaqatIter: `for` va `in` ishlaydi (iteratsiya orqali)
     • FaqatGetitem: hammasi ishlaydi, lekin Iterable EMAS
""")


print("=== 5. __reversed__ ===\n")


class BilanReversed:
    def __init__(self, el): self._el = list(el)
    def __len__(self): return len(self._el)
    def __getitem__(self, i): return self._el[i]
    def __reversed__(self):
        print(f"      __reversed__ chaqirildi")
        return reversed(self._el)


class ReversedSiz:
    def __init__(self, el): self._el = list(el)
    def __len__(self): return len(self._el)
    def __getitem__(self, i):
        print(f"      __getitem__({i})")
        return self._el[i]


print(f"  __reversed__ bor:")
print(f"    {list(reversed(BilanReversed([1, 2, 3])))}")

print(f"\n  __reversed__ yo'q (__len__ + __getitem__):")
print(f"    {list(reversed(ReversedSiz([1, 2, 3])))}")

print(f"""
  ⭐ reversed(x) MEXANIZMI:
     1. __reversed__()               bor bo'lsa
     2. __len__ + __getitem__        ikkalasi ham bor bo'lsa
        → x[len-1], x[len-2], ..., x[0]
     3. TypeError
""")


class FaqatIterSinf:
    def __iter__(self): return iter([1, 2, 3])


try:
    reversed(FaqatIterSinf())
except TypeError as e:
    print(f"  Faqat __iter__ bilan: ❌ TypeError: {e}")

Natijaning muhim qismi:

text
=== ⭐ 1. Eski iteratsiya protokoli ===

  for x in FaqatGetitem(3):
      __getitem__(0)
    → 0
      __getitem__(1)
    → 10
      __getitem__(3)

  isinstance(f, Iterable) = False   ⚠️ False, lekin iteratsiya ishlaydi!

=== 3. Iterable vs Iterator ===

  Sinf                 1-marta        2-marta        Ichma-ich
  ──────────────────────────────────────────────────────────────
  OziIterator          [0, 1, 2]      []             []
  AlohidaIterator      [0, 1, 2]      [0, 1, 2]      [(0, 0), (0, 1)]
  Generatorli          [0, 1, 2]      [0, 1, 2]      [(0, 0), (0, 1)]

=== 4. Protokol tekshiruvi ===

  Sinf               len()    in       for      [i]      Sized    Iterable
  ────────────────────────────────────────────────────────────────────────
  FaqatContains      ❌       ✅       ❌       ❌       ❌       ❌
  FaqatIter          ❌       ✅       ✅       ❌       ❌       ✅
  FaqatGetitem       ❌       ✅       ✅       ✅       ❌       ❌

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

Misol 2 — Kesish va indekslash

python
"""__getitem__ ning barcha holatlari."""

print("=== ⭐ 1. Uch xil indeks ===\n")


class Kuzatuvchi:
    """Qanday indeks kelayotganini ko'rsatadi."""

    def __getitem__(self, i):
        return f"{type(i).__name__}: {i!r}"


k = Kuzatuvchi()

INDEKSLAR = [
    "k[0]", "k[-1]", "k[1:3]", "k[::2]", "k[::-1]",
    "k[1:10:2]", "k['a']", "k[1, 2]", "k[1:2, 3:4]",
    "k[...]", "k[None]",
]

for kod in INDEKSLAR:
    print(f"  {kod:<16} → {eval(kod)}")

print(f"""
  ⭐ __getitem__ ISTALGAN obyektni oladi:
     int, slice, str, tuple, Ellipsis, None, ...

     NumPy aynan shundan foydalanadi:
       arr[1:3, 2:4]  →  tuple of slices
""")


print("=== 2. To'g'ri __getitem__ ===\n")


class Royxat:
    """✅ Barcha holatlar to'g'ri qayta ishlanadi."""

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

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

    def __getitem__(self, i):
        if isinstance(i, slice):
            return Royxat(self._el[i])              # ⭐ o'sha tur
        if isinstance(i, int):
            if i < 0:
                i += len(self._el)                  # ⭐ salbiy
            if not 0 <= i < len(self._el):
                raise IndexError(
                    f"indeks chegaradan tashqarida: {i}"
                )
            return self._el[i]
        raise TypeError(
            f"indeks int yoki slice bo'lsin, {type(i).__name__} emas"
        )

    def __setitem__(self, i, q):
        if isinstance(i, slice):
            self._el[i] = list(q)
        elif isinstance(i, int):
            if i < 0:
                i += len(self._el)
            if not 0 <= i < len(self._el):
                raise IndexError(f"indeks chegaradan tashqarida: {i}")
            self._el[i] = q
        else:
            raise TypeError("int yoki slice")

    def __delitem__(self, i):
        del self._el[i]

    def __iter__(self):
        return iter(self._el)

    def __contains__(self, x):
        return x in self._el

    def __repr__(self):
        return f"Royxat({self._el!r})"

    def __eq__(self, b):
        if isinstance(b, Royxat):
            return self._el == b._el
        if isinstance(b, list):
            return self._el == b
        return NotImplemented


r = Royxat(range(10))
print(f"  r = {r}\n")

AMALLAR = [
    ("r[0]",            lambda: r[0]),
    ("r[-1]",           lambda: r[-1]),
    ("r[-3]",           lambda: r[-3]),
    ("r[2:5]",          lambda: r[2:5]),
    ("r[::2]",          lambda: r[::2]),
    ("r[::-1]",         lambda: r[::-1]),
    ("r[-3:]",          lambda: r[-3:]),
    ("r[100]",          lambda: r[100]),
    ("r[-100]",         lambda: r[-100]),
    ("r['a']",          lambda: r["a"]),
    ("len(r)",          lambda: len(r)),
    ("5 in r",          lambda: 5 in r),
    ("list(r)[:3]",     lambda: list(r)[:3]),
    ("r == list(range(10))", lambda: r == list(range(10))),
]

for kod, f in AMALLAR:
    try:
        print(f"  {kod:<24} → {f()!r}")
    except (IndexError, TypeError) as e:
        print(f"  {kod:<24} → ❌ {type(e).__name__}: {e}")


print("\n=== ⭐ 3. slice.indices() ===\n")

s = slice(2, 8, 2)
print(f"  s = slice(2, 8, 2)")
print(f"    s.start, s.stop, s.step = {s.start}, {s.stop}, {s.step}")

print(f"\n  s.indices(uzunlik) — chegaralarni to'g'rilaydi:")
for uzunlik in [10, 5, 3, 0]:
    print(f"    s.indices({uzunlik:<3}) = {s.indices(uzunlik)}  "
          f"→ range{s.indices(uzunlik)}")

print(f"\n  Salbiy va chegaradan tashqari:")
for sl in [slice(-3, None), slice(None, -2), slice(None, None, -1),
           slice(100, 200), slice(-100, 100)]:
    print(f"    slice({sl.start}, {sl.stop}, {sl.step}).indices(10) = "
          f"{sl.indices(10)}")

print(f"""
  ⭐ AMALIY ISHLATISH:

     def __getitem__(self, i):
         if isinstance(i, slice):
             boshlanish, tugash, qadam = i.indices(len(self))
             return [self._ol(j) for j in range(boshlanish, tugash, qadam)]
""")


class Dangasa:
    """⭐ Elementlarni HISOBLAB beradi (saqlamaydi)."""

    def __init__(self, n, f):
        self.n, self.f = n, f

    def __len__(self):
        return self.n

    def __getitem__(self, i):
        if isinstance(i, slice):
            boshlanish, tugash, qadam = i.indices(self.n)
            return [self.f(j) for j in range(boshlanish, tugash, qadam)]
        if i < 0:
            i += self.n
        if not 0 <= i < self.n:
            raise IndexError(i)
        return self.f(i)

    def __repr__(self):
        namuna = [self.f(i) for i in range(min(3, self.n))]
        qolgan = f", ... ({self.n} ta)" if self.n > 3 else ""
        return f"Dangasa({namuna}{qolgan})"


kvadratlar = Dangasa(1_000_000, lambda i: i ** 2)

print(f"  Dangasa (1 million element, xotirada saqlanmaydi):")
print(f"    {kvadratlar}")
print(f"    len       = {len(kvadratlar):,}")
print(f"    [0]       = {kvadratlar[0]}")
print(f"    [999]     = {kvadratlar[999]}")
print(f"    [-1]      = {kvadratlar[-1]:,}")
print(f"    [10:15]   = {kvadratlar[10:15]}")
print(f"    [::250000] = {kvadratlar[::250000]}")

import sys
print(f"\n    Xotira: {sys.getsizeof(kvadratlar)} bayt")
print(f"    Ro'yxat bo'lganda: ~{1_000_000 * 28 / 1_048_576:.0f} MB")


print("\n\n=== 4. __setitem__ va __delitem__ ===\n")

r = Royxat(range(10))
print(f"  Boshida: {r}\n")

OZGARTIRISHLAR = [
    ("r[0] = 100",      lambda: r.__setitem__(0, 100)),
    ("r[-1] = 900",     lambda: r.__setitem__(-1, 900)),
    ("r[2:5] = [0,0]",  lambda: r.__setitem__(slice(2, 5), [0, 0])),
    ("del r[0]",        lambda: r.__delitem__(0)),
    ("del r[:2]",       lambda: r.__delitem__(slice(None, 2))),
    ("r[100] = 1",      lambda: r.__setitem__(100, 1)),
]

for kod, f in OZGARTIRISHLAR:
    try:
        f()
        print(f"  {kod:<20} → {r}")
    except (IndexError, TypeError) as e:
        print(f"  {kod:<20} → ❌ {type(e).__name__}: {e}")


print("\n=== ⭐ 5. __missing__ ===\n")


class Sukutli(dict):
    """⭐ FAQAT dict avlodlarida ishlaydi."""

    def __missing__(self, k):
        print(f"      __missing__({k!r})")
        return f"<{k} yo'q>"


class Hisoblagich(dict):
    def __missing__(self, k):
        self[k] = 0                             # ⭐ yaratadi
        return 0


d = Sukutli(a=1, b=2)
print(f"  d = {dict(d)}")
print(f"    d['a']       → {d['a']}")
print(f"    d['yoq']     → ", end="")
print(f"{d['yoq']!r}")
print(f"    d.get('yoq') → {d.get('yoq')!r}   ⚠️ __missing__ chaqirilmadi")
print(f"    'yoq' in d   → {'yoq' in d}   ⚠️ ham yo'q")

h = Hisoblagich()
for soz in "salom dunyo salom python dunyo salom".split():
    h[soz] += 1
print(f"\n  Hisoblagich: {dict(h)}")


class OddiySinf:
    """⚠️ dict dan meros olmagan — __missing__ ISHLAMAYDI."""

    def __init__(self): self._d = {}
    def __getitem__(self, k): return self._d[k]
    def __missing__(self, k): return "hech qachon chaqirilmaydi"


try:
    OddiySinf()["yoq"]
except KeyError:
    print(f"\n  OddiySinf()['yoq'] → ❌ KeyError")
    print(f"    ⚠️ __missing__ CHAQIRILMADI (dict avlodi emas)")

print(f"""
  ⭐ __missing__ — dict.__getitem__ ICHIDA chaqiriladi.
     Sizning __getitem__ ingiz uni o'zi chaqirishi kerak:

     def __getitem__(self, k):
         try:
             return self._d[k]
         except KeyError:
             if hasattr(self, "__missing__"):
                 return self.__missing__(k)
             raise
""")

Natijaning muhim qismi:

text
=== ⭐ 1. Uch xil indeks ===

  k[0]             → int: 0
  k[1:3]           → slice: slice(1, 3, None)
  k['a']           → str: 'a'
  k[1, 2]          → tuple: (1, 2)
  k[...]           → ellipsis: Ellipsis

=== 2. To'g'ri __getitem__ ===

  r[2:5]                   → Royxat([2, 3, 4])
  r[::-1]                  → Royxat([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
  r[100]                   → ❌ IndexError: indeks chegaradan tashqarida
  r['a']                   → ❌ TypeError: indeks int yoki slice bo'lsin

=== ⭐ 3. slice.indices() ===

  ...

  Dangasa (1 million element, xotirada saqlanmaydi):
    [-1]      = 999,998,000,001
    Xotira: 48 bayt
    Ro'yxat bo'lganda: ~27 MB

=== ⭐ 5. __missing__ ===

    d.get('yoq') → None   ⚠️ __missing__ chaqirilmadi
  Hisoblagich: {'salom': 3, 'dunyo': 2, 'python': 1}

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

Misol 3 — collections.abc

python
"""ABC dan meros — metodlar bepul."""

from collections.abc import (Sequence, MutableSequence, Mapping,
                             MutableMapping, Set, MutableSet)
from collections import UserDict, UserList

print("=== 1. Sequence — bepul metodlar ===\n")


class QoldaRoyxat:
    """⚠️ Hammasini qo'lda yozish kerak."""

    def __init__(self, el): self._el = list(el)
    def __getitem__(self, i): return self._el[i]
    def __len__(self): return len(self._el)


class AbcRoyxat(Sequence):
    """✅ Sequence dan meros."""

    def __init__(self, el): self._el = list(el)
    def __getitem__(self, i): return self._el[i]
    def __len__(self): return len(self._el)


q, a = QoldaRoyxat([1, 2, 3, 2]), AbcRoyxat([1, 2, 3, 2])

METODLAR = ["__contains__", "__iter__", "__reversed__", "index", "count"]

print(f"  {'Metod':<16} {'QoldaRoyxat':<16} {'AbcRoyxat'}")
print("  " + "─" * 46)
for m in METODLAR:
    qq = "✅" if hasattr(q, m) else "❌"
    aa = "✅" if hasattr(a, m) else "❌"
    print(f"  {m:<16} {qq:<16} {aa}")

print(f"\n  AbcRoyxat imkoniyatlari:")
print(f"    2 in a           = {2 in a}")
print(f"    a.index(3)       = {a.index(3)}")
print(f"    a.count(2)       = {a.count(2)}")
print(f"    list(reversed(a)) = {list(reversed(a))}")
print(f"    isinstance(a, Sequence) = {isinstance(a, Sequence)}")


print("\n=== 2. MutableSequence ===\n")


class Stack(MutableSequence):
    """✅ Uch metod → to'liq ro'yxat."""

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

    # ── Majburiy (5 ta) ──
    def __getitem__(self, i): return self._el[i]
    def __setitem__(self, i, q): self._el[i] = q
    def __delitem__(self, i): del self._el[i]
    def __len__(self): return len(self._el)
    def insert(self, i, q): self._el.insert(i, q)

    def __repr__(self): return f"Stack({self._el!r})"


s = Stack([1, 2, 3])
print(f"  s = {s}\n")

BEPUL = [
    ("s.append(4)",         lambda: s.append(4)),
    ("s.extend([5, 6])",    lambda: s.extend([5, 6])),
    ("s.pop()",             lambda: s.pop()),
    ("s.pop(0)",            lambda: s.pop(0)),
    ("s.remove(3)",         lambda: s.remove(3)),
    ("s.insert(0, 99)",     lambda: s.insert(0, 99)),
    ("s.reverse()",         lambda: s.reverse()),
    ("s += [7]",            lambda: s.__iadd__([7])),
    ("s.index(99)",         lambda: s.index(99)),
    ("s.count(99)",         lambda: s.count(99)),
    ("99 in s",             lambda: 99 in s),
]

for kod, f in BEPUL:
    natija = f()
    korinish = f" → {natija!r}" if natija is not None and \
        not isinstance(natija, Stack) else ""
    print(f"  {kod:<20}{korinish:<12} s = {s}")

print(f"""
  ⭐ 5 metod yozdik, 11+ metod BEPUL oldik.
""")


print("=== 3. Mapping ===\n")


class OqishLugat(Mapping):
    """✅ Faqat o'qish uchun lug'at."""

    def __init__(self, **kw):
        self._d = dict(kw)

    def __getitem__(self, k): return self._d[k]
    def __iter__(self): return iter(self._d)
    def __len__(self): return len(self._d)

    def __repr__(self): return f"OqishLugat({self._d!r})"


o = OqishLugat(a=1, b=2, c=3)
print(f"  o = {o}\n")

for kod, f in [
    ("o['a']",              lambda: o["a"]),
    ("o.get('a')",          lambda: o.get("a")),
    ("o.get('yoq', 0)",     lambda: o.get("yoq", 0)),
    ("'a' in o",            lambda: "a" in o),
    ("list(o.keys())",      lambda: list(o.keys())),
    ("list(o.values())",    lambda: list(o.values())),
    ("list(o.items())",     lambda: list(o.items())),
    ("len(o)",              lambda: len(o)),
    ("dict(o)",             lambda: dict(o)),
    ("o == {'a':1,'b':2,'c':3}", lambda: o == {"a": 1, "b": 2, "c": 3}),
]:
    print(f"  {kod:<28} → {f()!r}")

print(f"\n  O'zgartirish:")
def _yoz():
    o["d"] = 4          # ⚠️ o.__setitem__(...) emas — Mapping da bu metod YO'Q
                        #    (u AttributeError berardi); sintaksis TypeError beradi


def _ochir():
    del o["a"]


for kod, f in [
    ("o['d'] = 4",  _yoz),
    ("del o['a']",  _ochir),
]:
    try:
        f()
        print(f"    {kod:<16} ⚠️ o'tdi")
    except TypeError as e:
        print(f"    {kod:<16} ✅ TypeError: {e}")


print("\n=== 4. Set ===\n")


class Diapazon(Set):
    """⭐ Diapazonni to'plam sifatida — elementlar saqlanmaydi."""

    def __init__(self, boshlanish, tugash):
        self.boshlanish, self.tugash = boshlanish, tugash

    def __contains__(self, x):
        return (isinstance(x, int)
                and self.boshlanish <= x < self.tugash)

    def __iter__(self):
        return iter(range(self.boshlanish, self.tugash))

    def __len__(self):
        return max(0, self.tugash - self.boshlanish)

    def __repr__(self):
        return f"Diapazon({self.boshlanish}, {self.tugash})"

    @classmethod
    def _from_iterable(cls, it):
        # ⚠️ Set ning &, |, -, ^ metodlari natijani cls(it) bilan yasaydi.
        #    Bizning konstruktor (boshlanish, tugash) kutadi → TypeError.
        #    Shuning uchun natijani oddiy frozenset qilib qaytaramiz.
        return frozenset(it)


d1, d2 = Diapazon(0, 10), Diapazon(5, 15)

print(f"  d1 = {d1}, d2 = {d2}\n")

for kod, f in [
    ("5 in d1",             lambda: 5 in d1),
    ("len(d1)",             lambda: len(d1)),
    ("d1 & d2",             lambda: d1 & d2),
    ("d1 | d2",             lambda: d1 | d2),
    ("d1 - d2",             lambda: d1 - d2),
    ("d1 ^ d2",             lambda: d1 ^ d2),
    ("d1 <= d2",            lambda: d1 <= d2),
    ("d1.isdisjoint(Diapazon(20,30))",
     lambda: d1.isdisjoint(Diapazon(20, 30))),
    ("set(d1) == set(range(10))", lambda: set(d1) == set(range(10))),
]:
    natija = f()
    if isinstance(natija, (set, frozenset)) and len(natija) > 6:
        natija = f"{{{', '.join(map(str, sorted(natija)[:5]))}, ...}}"
    print(f"  {kod:<34} → {natija!r}")

print(f"""
  ⭐ __contains__, __iter__, __len__ yozdik —
     BARCHA to'plam amallari bepul.

  ⚠️ Natija — oddiy set (yangi Diapazon emas).
     _from_iterable() ni qayta aniqlash mumkin.
""")


print("=== ⚠️ 5. dict dan meros — tuzoq ===\n")


class YomonSozlama(dict):
    """⚠️ Kalitlarni kichik harfga o'girmoqchi."""

    def __setitem__(self, k, v):
        super().__setitem__(k.lower(), v)


class YaxshiSozlama(UserDict):
    """✅ UserDict — sof Python."""

    def __setitem__(self, k, v):
        super().__setitem__(k.lower(), v)


print(f"  {'Amal':<28} {'dict avlodi':<24} {'UserDict avlodi'}")
print("  " + "─" * 74)

SINOVLAR = [
    ("Sinf(A=1)",           lambda S: dict(S(A=1))),
    ("s['B'] = 2",          None),
    ("s.update(C=3)",       None),
    ("s.setdefault('D', 4)", None),
]

y1, y2 = YomonSozlama(A=1), YaxshiSozlama(A=1)
print(f"  {'Sinf(A=1)':<28} {str(dict(y1)):<24} {dict(y2)}")

y1["B"] = 2; y2["B"] = 2
kod = "s['B'] = 2"
print(f"  {kod:<28} {str(dict(y1)):<24} {dict(y2)}")

y1.update(C=3); y2.update(C=3)
print(f"  {'s.update(C=3)':<28} {str(dict(y1)):<24} {dict(y2)}")

y1.setdefault("D", 4); y2.setdefault("D", 4)
kod = "s.setdefault('D', 4)"
print(f"  {kod:<28} {str(dict(y1)):<24} {dict(y2)}")

print(f"""
  ⚠️ dict AVLODI:
     • __init__, update, setdefault — C da yozilgan
     • Ular __setitem__ ni CHAQIRMAYDI
     • Faqat s['B'] = 2 ishlaydi

  ✅ UserDict:
     • Sof Python, self.data lug'atini o'raydi
     • Barcha metodlar __setitem__ orqali o'tadi
""")


print("=== 6. Amaliy: normallashtiruvchi lug'at ===\n")


class HttpSarlavhalar(UserDict):
    """HTTP sarlavhalari — katta-kichik harf farqlanmaydi."""

    def __setitem__(self, k, v):
        super().__setitem__(self._normalla(k), v)

    def __getitem__(self, k):
        return super().__getitem__(self._normalla(k))

    def __delitem__(self, k):
        super().__delitem__(self._normalla(k))

    def __contains__(self, k):
        return self._normalla(k) in self.data

    @staticmethod
    def _normalla(k: str) -> str:
        return "-".join(q.capitalize() for q in str(k).split("-"))


h = HttpSarlavhalar()
h["content-type"] = "application/json"
h["CONTENT-LENGTH"] = "42"
h.update({"x-custom-header": "qiymat"})

print(f"  Kiritildi: 'content-type', 'CONTENT-LENGTH', 'x-custom-header'")
print(f"  Saqlandi:")
for k, v in h.items():
    print(f"    {k:<22} {v}")

print(f"\n  Har xil yozuvda o'qish:")
for k in ["Content-Type", "content-type", "CONTENT-TYPE", "cOnTeNt-TyPe"]:
    print(f"    h[{k!r:<16}] = {h[k]!r}")

print(f"\n  Boshqa amallar:")
print(f"    'content-type' in h     = {'content-type' in h}")
print(f"    h.get('YOQ', 'sukut')   = {h.get('YOQ', 'sukut')!r}")
print(f"    len(h)                  = {len(h)}")
del h["CONTENT-length"]
print(f"    del h['CONTENT-length'] → {list(h.keys())}")

print(f"""
  ⭐ UserDict tufayli:
     • __init__, update, setdefault — hammasi ishlaydi
     • Faqat 4 metod qayta aniqlandi
     • self.data — asl lug'at
""")

Natijaning muhim qismi:

text
=== 1. Sequence — bepul metodlar ===

  Metod            QoldaRoyxat      AbcRoyxat
  ──────────────────────────────────────────────
  __contains__     ❌                ✅
  __reversed__     ❌               ✅
  index            ❌               ✅
  count            ❌               ✅

=== 2. MutableSequence ===

  s.append(4)                      s = Stack([1, 2, 3, 4])
  s.pop()             → 6          s = Stack([1, 2, 3, 4, 5])
  s += [7]                         s = Stack([5, 4, 2, 99, 7])

  ⭐ 5 metod yozdik, 11+ metod BEPUL oldik.

=== ⚠️ 5. dict dan meros — tuzoq ===

  Amal                         dict avlodi              UserDict avlodi
  ──────────────────────────────────────────────────────────────────────
  Sinf(A=1)                    {'A': 1}                 {'a': 1}
  s['B'] = 2                   {'A': 1, 'b': 2}         {'a': 1, 'b': 2}
  s.update(C=3)                {'A': 1, 'b': 2, 'C': 3} {'a': 1, ..., 'c': 3}

Nima ko'rsatdi: 2.7-bo'lim.

Misol 4 — Amaliy: matn korpusi

python
"""To'liq konteyner — barcha protokollar birga."""

from __future__ import annotations

import re
from collections.abc import Sequence, Mapping
from dataclasses import dataclass, field
from typing import Iterator, Any

print("=== MATN KORPUSI ===\n")


@dataclass(frozen=True, slots=True)
class Hujjat:
    """Korpusdagi bitta hujjat."""

    id: int
    sarlavha: str
    matn: str
    teglar: frozenset[str] = frozenset()

    @property
    def sozlar(self) -> tuple[str, ...]:
        return tuple(re.findall(r"[^\W\d_]+", self.matn.lower()))

    def __len__(self) -> int:
        return len(self.sozlar)

    def __contains__(self, soz: str) -> bool:
        return soz.lower() in self.sozlar

    def __repr__(self) -> str:
        return f"Hujjat(#{self.id}, {self.sarlavha!r}, {len(self)} so'z)"


class Korpus(Sequence):
    """Hujjatlar to'plami.

    PROTOKOLLAR:
      Sequence      — [i], kesish, iteratsiya, index, count
      Mapping-kabi  — ["sarlavha"] bilan ham topish
      Container     — hujjat yoki so'z bo'yicha `in`
      Sized         — len()
    """

    def __init__(self, hujjatlar=()):
        self._h: list[Hujjat] = list(hujjatlar)
        self._indeks: dict[str, set[int]] | None = None

    # ── Sequence protokoli ──

    def __len__(self) -> int:
        return len(self._h)

    def __getitem__(self, i):
        """⭐ int, slice va str ni qo'llab-quvvatlaydi."""
        if isinstance(i, slice):
            return Korpus(self._h[i])                   # ⭐ o'sha tur
        if isinstance(i, int):
            if i < 0:
                i += len(self._h)
            if not 0 <= i < len(self._h):
                raise IndexError(f"indeks chegaradan tashqarida: {i}")
            return self._h[i]
        if isinstance(i, str):
            for h in self._h:
                if h.sarlavha == i:
                    return h
            raise KeyError(f"sarlavha topilmadi: {i!r}")
        raise TypeError(
            f"indeks int, slice yoki str bo'lsin, "
            f"{type(i).__name__} emas"
        )

    def __contains__(self, x) -> bool:
        """⭐ Hujjat, sarlavha yoki so'z bo'yicha."""
        if isinstance(x, Hujjat):
            return x in self._h
        if isinstance(x, str):
            return (any(h.sarlavha == x for h in self._h)
                    or any(x.lower() in h for h in self._h))
        return False

    def __iter__(self) -> Iterator[Hujjat]:
        return iter(self._h)

    def __reversed__(self) -> Iterator[Hujjat]:
        return reversed(self._h)

    # ── O'zgartirish ──

    def qosh(self, h: Hujjat) -> "Korpus":
        if not isinstance(h, Hujjat):
            raise TypeError(f"Hujjat kutildi: {type(h).__name__}")
        self._h.append(h)
        self._indeks = None                             # kesh bekor
        return self

    def __iadd__(self, boshqa) -> "Korpus":
        """⭐ korpus += [hujjat, ...]"""
        for h in boshqa:
            self.qosh(h)
        return self                                     # ⭐ SHART

    def __add__(self, boshqa) -> "Korpus":
        """⭐ YANGI korpus."""
        if isinstance(boshqa, Korpus):
            return Korpus(self._h + boshqa._h)
        if isinstance(boshqa, (list, tuple)):
            return Korpus(self._h + list(boshqa))
        return NotImplemented

    # ── To'plam kabi amallar ──

    def __and__(self, boshqa: "Korpus") -> "Korpus":
        """Umumiy hujjatlar."""
        if not isinstance(boshqa, Korpus):
            return NotImplemented
        idlar = {h.id for h in boshqa}
        return Korpus(h for h in self._h if h.id in idlar)

    def __or__(self, boshqa: "Korpus") -> "Korpus":
        if not isinstance(boshqa, Korpus):
            return NotImplemented
        korilgan, natija = set(), []
        for h in list(self._h) + list(boshqa):
            if h.id not in korilgan:
                korilgan.add(h.id)
                natija.append(h)
        return Korpus(natija)

    def __sub__(self, boshqa: "Korpus") -> "Korpus":
        if not isinstance(boshqa, Korpus):
            return NotImplemented
        idlar = {h.id for h in boshqa}
        return Korpus(h for h in self._h if h.id not in idlar)

    # ── Qidiruv ──

    def _indeksni_qur(self) -> dict[str, set[int]]:
        if self._indeks is None:
            ind: dict[str, set[int]] = {}
            for h in self._h:
                for soz in set(h.sozlar):
                    ind.setdefault(soz, set()).add(h.id)
            self._indeks = ind
        return self._indeks

    def qidir(self, *sozlar: str) -> "Korpus":
        """Barcha so'zlar bor hujjatlar."""
        ind = self._indeksni_qur()
        if not sozlar:
            return Korpus(self._h)
        natija = ind.get(sozlar[0].lower(), set())
        for s in sozlar[1:]:
            natija &= ind.get(s.lower(), set())
        return Korpus(h for h in self._h if h.id in natija)

    def teg_boyicha(self, *teglar: str) -> "Korpus":
        kerak = frozenset(t.lower() for t in teglar)
        return Korpus(h for h in self._h if kerak <= h.teglar)

    def filtrla(self, shart) -> "Korpus":
        return Korpus(h for h in self._h if shart(h))

    # ── Statistika ──

    @property
    def sozlar_soni(self) -> int:
        return sum(len(h) for h in self._h)

    def chastota(self, n: int = 10) -> list[tuple[str, int]]:
        hisob: dict[str, int] = {}
        for h in self._h:
            for s in h.sozlar:
                hisob[s] = hisob.get(s, 0) + 1
        return sorted(hisob.items(), key=lambda x: (-x[1], x[0]))[:n]

    # ── Ko'rinish ──

    def __repr__(self) -> str:
        if len(self._h) <= 2:
            ichki = ", ".join(repr(h) for h in self._h)
        else:
            ichki = (f"{self._h[0]!r}, ... yana {len(self._h) - 1}")
        return f"Korpus([{ichki}])"

    def __str__(self) -> str:
        return f"Korpus: {len(self._h)} hujjat, {self.sozlar_soni} so'z"

    def __bool__(self) -> bool:
        return bool(self._h)

    def __eq__(self, b) -> bool:
        if not isinstance(b, Korpus):
            return NotImplemented
        return [h.id for h in self._h] == [h.id for h in b._h]


HUJJATLAR = [
    Hujjat(1, "Python asoslari",
           "Python oddiy va kuchli dasturlash tili. "
           "Python o'rganish oson.",
           frozenset({"python", "darslik"})),
    Hujjat(2, "JavaScript kirish",
           "JavaScript veb dasturlash uchun til. "
           "JavaScript brauzerda ishlaydi.",
           frozenset({"javascript", "veb"})),
    Hujjat(3, "Ma'lumot tuzilmalari",
           "Ro'yxat va lug'at Python dagi asosiy tuzilmalar. "
           "Ular dasturlash uchun muhim.",
           frozenset({"python", "algoritm", "darslik"})),
    Hujjat(4, "Veb dasturlash",
           "Veb dasturlash uchun Python va JavaScript ishlatiladi.",
           frozenset({"python", "javascript", "veb"})),
    Hujjat(5, "Algoritmlar",
           "Saralash va qidirish algoritmlari dasturlash asosi.",
           frozenset({"algoritm"})),
]

k = Korpus(HUJJATLAR)


print("1. Sequence protokoli:\n")

for kod, f in [
    ("len(k)",              lambda: len(k)),
    ("k[0]",                lambda: k[0]),
    ("k[-1]",               lambda: k[-1]),
    ("k[1:3]",              lambda: k[1:3]),
    ("k[::2]",              lambda: k[::2]),
    ("k['Algoritmlar']",    lambda: k["Algoritmlar"]),
    ("k.index(HUJJATLAR[2])", lambda: k.index(HUJJATLAR[2])),
    ("k.count(HUJJATLAR[0])", lambda: k.count(HUJJATLAR[0])),
    ("bool(k)",             lambda: bool(k)),
    ("bool(Korpus())",      lambda: bool(Korpus())),
]:
    print(f"  {kod:<24} → {f()!r}")

print(f"\n  Xatolar:")
for kod, f in [
    ("k[100]",      lambda: k[100]),
    ("k['yoq']",    lambda: k["yoq"]),
    ("k[1.5]",      lambda: k[1.5]),
]:
    try:
        f()
        print(f"    {kod:<16} ⚠️")
    except (IndexError, KeyError, TypeError) as e:
        print(f"    {kod:<16} ✅ {type(e).__name__}: {str(e)[:40]}")


print("\n\n2. ⭐ __contains__ — uch xil:\n")

for x, izoh in [
    (HUJJATLAR[0], "Hujjat obyekti"),
    ("Algoritmlar", "sarlavha"),
    ("python", "so'z (matn ichida)"),
    ("javascript", "so'z"),
    ("rust", "yo'q so'z"),
]:
    korinish = repr(x) if not isinstance(x, Hujjat) else "HUJJATLAR[0]"
    print(f"  {korinish:<18} in k → {x in k!s:<7} ({izoh})")


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

print(f"  for h in k:")
for h in k:
    print(f"    {h}")

print(f"\n  reversed(k):")
for h in list(reversed(k))[:2]:
    print(f"    {h}")

print(f"\n  Comprehension:")
uzunlar = [h.sarlavha for h in k if len(h) > 8]
print(f"    8 dan uzun: {uzunlar}")


print("\n\n4. Amallar:\n")

python_k = k.teg_boyicha("python")
veb_k = k.teg_boyicha("veb")

print(f"  python teglilar: {[h.id for h in python_k]}")
print(f"  veb teglilar:    {[h.id for h in veb_k]}\n")

for kod, f in [
    ("python_k & veb_k",    lambda: python_k & veb_k),
    ("python_k | veb_k",    lambda: python_k | veb_k),
    ("python_k - veb_k",    lambda: python_k - veb_k),
    ("k[:2] + k[3:]",       lambda: k[:2] + k[3:]),
]:
    natija = f()
    print(f"  {kod:<22} → {[h.id for h in natija]}")

print(f"\n  += bilan:")
yangi = Korpus(k[:2])
print(f"    yangi = Korpus(k[:2])  → {[h.id for h in yangi]}")
yangi += [HUJJATLAR[4]]
print(f"    yangi += [HUJJATLAR[4]] → {[h.id for h in yangi]}")


print("\n\n5. Qidiruv:\n")

SOROVLAR = [
    ("python",),
    ("javascript",),
    ("python", "javascript"),
    ("dasturlash",),
    ("dasturlash", "python"),
    ("rust",),
]

for sorov in SOROVLAR:
    natija = k.qidir(*sorov)
    idlar = [h.id for h in natija]
    print(f"  qidir{sorov} → {idlar if idlar else '❌ topilmadi'}")

print(f"\n  Teg bo'yicha:")
for teglar in [("python",), ("darslik",), ("python", "darslik"),
               ("python", "javascript")]:
    natija = k.teg_boyicha(*teglar)
    print(f"    {str(teglar):<26} → {[h.id for h in natija]}")

print(f"\n  Filtrlash:")
uzun = k.filtrla(lambda h: len(h) >= 10)
print(f"    len(h) >= 10 → {[h.id for h in uzun]}")


print("\n\n6. Statistika:\n")

print(f"  {k}")
print(f"    Hujjatlar:  {len(k)}")
print(f"    So'zlar:    {k.sozlar_soni}")
print(f"    O'rtacha:   {k.sozlar_soni / len(k):.1f} so'z/hujjat")

print(f"\n  Eng ko'p uchraydigan so'zlar:")
for soz, n in k.chastota(8):
    print(f"    {soz:<16} {'█' * n} {n}")

print(f"\n  Indeks hajmi: {len(k._indeksni_qur())} noyob so'z")


print("\n\n7. ABC tekshiruvi:\n")

from collections.abc import (Iterable, Container, Sized, Collection,
                             Reversible, Sequence as SeqABC)

print(f"  {'Protokol':<16} {'Korpus':<10} {'Hujjat'}")
print("  " + "─" * 38)
for A in (Sized, Iterable, Container, Collection, Reversible, SeqABC):
    kk = "✅" if isinstance(k, A) else "❌"
    hh = "✅" if isinstance(HUJJATLAR[0], A) else "❌"
    print(f"  {A.__name__:<16} {kk:<10} {hh}")

print(f"""

  ⭐ BU SINFDA:

     Sequence dan meros:
       __getitem__, __len__ yozildi
       index, count BEPUL olindi

     Qayta aniqlangan (samaradorlik/qulaylik uchun):
       __contains__   uch xil qidiruv
       __iter__       to'g'ridan-to'g'ri
       __reversed__   to'g'ridan-to'g'ri

     Qo'shimcha protokollar:
       __add__, __iadd__          birlashtirish
       __and__, __or__, __sub__   to'plam amallari
       __bool__, __eq__           mantiq
       __repr__, __str__          ko'rinish

     ⭐ __getitem__ UCH turni qabul qiladi:
        int    → Hujjat
        slice  → Korpus (o'sha tur!)
        str    → sarlavha bo'yicha Hujjat
""")

Natijaning muhim qismi:

text
1. Sequence protokoli:

  k[1:3]                   → Korpus([Hujjat(#2, ...), Hujjat(#3, ...)])
  k['Algoritmlar']         → Hujjat(#5, 'Algoritmlar', 6 so'z)
  k.index(HUJJATLAR[2])    → 2
  bool(Korpus())           → False

    k[1.5]           ✅ TypeError: indeks int, slice yoki str bo'lsin

2. ⭐ __contains__ — uch xil:

  HUJJATLAR[0]       in k → True    (Hujjat obyekti)
  'Algoritmlar'      in k → True    (sarlavha)
  'python'           in k → True    (so'z (matn ichida))
  'rust'             in k → False   (yo'q so'z)

5. Qidiruv:

  qidir('python',) → [1, 3, 4]
  qidir('python', 'javascript') → [4]
  qidir('rust',) → ❌ topilmadi

7. ABC tekshiruvi:

  Protokol         Korpus     Hujjat
  ──────────────────────────────────────
  Sized            ✅         ✅
  Iterable         ✅         ❌
  Container        ✅         ✅
  Sequence         ✅         ❌

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


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"for uchun __iter__ shart" __getitem__ ham yetadi (eski protokol)
"__getitem__ bo'lsa Iterable" isinstance(x, Iterable) — False
"__iter__ self qaytarishi kerak" Faqat iterator shunday qiladi
"__contains__ bo'lsa for ishlaydi" Alohida protokollar
"__len__ bo'lsa bool True" len() == 0 → False
"__missing__ har sinfda ishlaydi" Faqat dict avlodlarida
"dict dan meros olsa bo'ladi" C metodlari __setitem__ ni chaqirmaydi
"Kesish list qaytarishi kerak" O'sha turni qaytaring

6. Keng tarqalgan xatolar va yechimlari

1. Kesishni qo'llab-quvvatlamaslik

python
def __getitem__(self, i):
    return self._el[i]          # ⚠️ kesish list qaytaradi
    if isinstance(i, slice):    # ✅
        return type(self)(self._el[i])

2. Salbiy indeks

python
if i >= len(self._el): raise IndexError    # ⚠️ -1 o'tib ketadi
if i < 0: i += len(self._el)               # ✅

3. __iter__ da self qaytarish

python
def __iter__(self): return self             # ⚠️ bir martalik
def __iter__(self): yield from self._el     # ✅

4. KeyError bilan eski protokol

python
def __getitem__(self, k): return self._d[k] # ⚠️ for cheksiz
def __iter__(self): return iter(self._d)    # ✅ aniq yozing

5. dict dan meros

python
class A(dict): ...                          # ⚠️ update ishlamaydi
class A(UserDict): ...                      # ✅

6. __len__ va __bool__ chalkashligi

python
if sozlama:                                 # ⚠️ bo'sh bo'lsa False
if sozlama is not None:                     # ✅

7. __iadd__ da return yo'q

python
def __iadd__(self, b):
    self._el.extend(b)                      # ⚠️ None
    return self                             # ✅

8. ABC ni ishlatmaslik

python
class A:                                    # ⚠️ 10 metod qo'lda
    ...
class A(Sequence):                          # ✅ 2 metod
    ...

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 6-qism (o'tilgan): list, dict, set — bularning hammasi shu protokollar
  • 6.17-dars: generator ifodalari
  • 8.9-dars: Protocol, ABC
  • 10-qism: iteratorlar va generatorlar — chuqur
  • collections.abc, collections — UserDict, UserList, UserString
  • NumPy, pandas: __getitem__ bilan murakkab indekslash
  • PEP 234: iteratsiya protokoli

8. Eng yaxshi amaliyotlar

  1. __iter__ ni aniq yozing. Eski protokolga tayanmang.

  2. collections.abc dan meros oling. O'nlab metod bepul.

  3. Kesishda o'sha turni qaytaring. type(self)(...).

  4. Salbiy indekslarni qo'llab-quvvatlang. if i < 0: i += len(self).

  5. Noto'g'ri indeks turida TypeError. Aniq xabar bilan.

  6. dict/list o'rniga UserDict/UserList. Yoki kompozitsiya.

  7. __iter__ generator bilan. Eng oson va to'g'ri yo'l.

  8. __len__ bilan __bool__ ni o'ylang. Bo'sh konteyner False bo'lishi kutilganmi?


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  class A:
        def __getitem__(self, i):
            if i >= 3: raise IndexError
            return i
    print(list(A()), 2 in A())
2.  from collections.abc import Iterable
    class A:
        def __getitem__(self, i): raise IndexError
    print(isinstance(A(), Iterable))
3.  class A:
        def __len__(self): return 0
    print(bool(A()))
4.  class A:
        def __contains__(self, x): return True
    try: print(list(A()))
    except TypeError: print("TypeError")
5.  class A:
        def __init__(self): self.i = 0
        def __iter__(self): return self
        def __next__(self):
            if self.i >= 2: raise StopIteration
            self.i += 1
            return self.i
    a = A()
    print(list(a), list(a))
6.  class A:
        def __getitem__(self, i): return i
    print(A()[1:3])
7.  print(slice(1, 10, 2).indices(5))
8.  class A(dict):
        def __setitem__(self, k, v): super().__setitem__(k.upper(), v)
    a = A(x=1)
    print(dict(a))
9.  from collections import UserDict
    class A(UserDict):
        def __setitem__(self, k, v): super().__setitem__(k.upper(), v)
    print(dict(A(x=1)))
10. class A(dict):
        def __missing__(self, k): return k * 2
    a = A()
    print(a["ab"], a.get("ab"))
11. from collections.abc import Sequence
    class A(Sequence):
        def __getitem__(self, i): return [1,2,3][i]
        def __len__(self): return 3
    print(A().count(2), list(reversed(A())))
12. class A:
        def __len__(self): return 3
    try: print(A()[0])
    except TypeError: print("TypeError")
Javoblar
  1. [0, 1, 2] True
  2. False — Iterable faqat __iter__ ni tekshiradi
  3. False
  4. TypeError — __iter__ ham, __getitem__ ham yo'q
  5. [1, 2] [] — o'zi iterator, bir martalik
  6. slice(1, 3, None) — slice obyekti qaytdi
  7. (1, 5, 2)
  8. {'x': 1} — __init__ __setitem__ ni chaqirmadi
  9. {'X': 1} —
  10. abab None — get __missing__ ni chaqirmaydi
  11. 1 [3, 2, 1]
  12. TypeError

Vazifa 2: Xatolarni tuzating

python
1.  def __getitem__(self, i): return self._el[i]      # kesish
2.  def __getitem__(self, i):
        if i >= len(self._el): raise IndexError
        return self._el[i]
3.  def __iter__(self): return self
4.  class Lugat:
        def __getitem__(self, k): return self._d[k]
5.  class Sozlama(dict):
        def __setitem__(self, k, v): super().__setitem__(k.lower(), v)
6.  def __iadd__(self, b): self._el.extend(b)
7.  class A:
        def __getitem__(self, i): return self._el[i]
        # index, count, __contains__ qo'lda yozilgan
8.  if sozlama:  # bo'sh bo'lsa ham davom etsin
Javoblar
python
1.  if isinstance(i, slice): return type(self)(self._el[i])
2.  if i < 0: i += len(self._el)  qo'shing
3.  yield from self._el  (yoki alohida iterator sinfi)
4.  __iter__ ni aniq yozing: return iter(self._d)
5.  class Sozlama(UserDict)
6.  return self  qo'shing
7.  class A(Sequence)  — bepul metodlar
8.  if sozlama is not None:

Vazifa 3: Halqa (ring buffer)

Yozing:

  1. Belgilangan hajm, to'lganda eng eskisini o'chiradi
  2. __len__, __getitem__ (kesish bilan), __iter__
  3. __setitem__, append, extend
  4. __contains__, __reversed__
  5. MutableSequence dan meros
  6. __repr__ — to'lganini ko'rsatsin

Vazifa 4: Ikki tomonlama lug'at

  1. kalit → qiymat va qiymat → kalit
  2. MutableMapping dan meros
  3. teskari xususiyati
  4. Takroriy qiymat — ValueError
  5. __setitem__ ikkalasini yangilasin
  6. UserDict bilan ham yozing, solishtiring

Vazifa 5: Dangasa fayl

  1. Katta faylni qatorma-qator o'qish
  2. __len__ — qatorlar soni (keshlangan)
  3. __getitem__ — n-qator (indeks bilan tez)
  4. Kesish qo'llab-quvvatlansin
  5. __iter__ — xotirani to'ldirmasin
  6. __contains__ — matn qidirish

Vazifa 6: Ichma-ich lug'at

  1. d["a.b.c"] → d["a"]["b"]["c"]
  2. __setitem__ yo'lni yaratsin
  3. __delitem__, __contains__
  4. __iter__ — barcha yo'llarni bersin
  5. tekis() va chuqur() metodlari
  6. MutableMapping dan meros

Vazifa 7: O'ylash

Nega Python __getitem__ orqali iteratsiyani (eski protokol) hali ham qo'llab-quvvatlaydi — nega uni olib tashlamadi?

Javob

Chunki orqaga moslik — Pythonning eng muhim kafolatlaridan biri, va olib tashlash foydasi zaridan kam.

1. Tarixiy kontekst

Python 2.0 (2000) gacha iteratsiya protokoli yo'q edi. for shunday ishlardi:

python
for x in obj:
    ...

# Aslida:
i = 0
while True:
    try:
        x = obj[i]
    except IndexError:
        break
    ...
    i += 1

Ya'ni __getitem__ — yagona iteratsiya usuli edi.

PEP 234 (2001, Python 2.2) __iter__/__next__ protokolini qo'shdi. Sabablari:

  1. Samaradorlik — indeks hisoblash kerak emas
  2. Ketma-ketlik bo'lmagan turlar — dict, set, file
  3. Cheksiz iteratsiya — generatorlar
  4. Holat saqlash — iterator o'z holatini biladi

Lekin eski protokol saqlanib qoldi.

2. Nima uchun olib tashlanmadi

a) Buzilgan kod hajmi

python
# 2000-2002 yillarda yozilgan minglab sinflar:
class MeningRoyxatim:
    def __getitem__(self, i): ...
    def __len__(self): ...
    # __iter__ YO'Q — o'sha paytda kerak emas edi

Bunday kod hali ham ishlaydi — ilmiy kutubxonalar, ichki korporativ kodlar, eski loyihalar.

b) Python 3 ga o'tish tajribasi

Python 2 → 3 o'tishi 11 yil davom etdi (2008-2020). Jamoa buzilishlarga juda sezgir bo'lib qoldi.

Guido va boshqa asosiy ishlab chiquvchilar bir necha marta aytdi: "biz yana bir Python 3 ni qilmaymiz".

c) Foydasi kam

Olib tashlash nima beradi?

  • Kod soddalashadi (iter() da bitta shox kamayadi) — juda kichik
  • Xatolar erta aniqlanadi — lekin mypy buni allaqachon qiladi

Nima yo'qotamiz?

  • Ishlaydigan kod buziladi
  • Foydalanuvchilar ishonchi

Bu — noto'g'ri savdo.

3. Amalda hali ham foydali

Ba'zi hollarda __getitem__ — tabiiy interfeys:

python
class Dangasa:
    """Hisoblanadigan ketma-ketlik."""

    def __init__(self, n, f): self.n, self.f = n, f
    def __len__(self): return self.n
    def __getitem__(self, i):
        if i >= self.n: raise IndexError
        return self.f(i)


kvadratlar = Dangasa(10**6, lambda i: i**2)
kvadratlar[500]                 # ✅ to'g'ridan-to'g'ri
for x in kvadratlar: ...        # ✅ ham ishlaydi

__iter__ yozish ortiqcha bo'lardi — __getitem__ allaqachon hamma narsani biladi.

4. Lekin tuzoqlari bor

python
class Lugat:
    def __getitem__(self, k): return self._d[k]

for x in Lugat():
    ...

Lugat()[0] → KeyError, IndexError emas → tsikl to'xtamaydi va KeyError chiqadi.

Xato xabari chalkash:

KeyError: 0

Foydalanuvchi "nega 0 kaliti qidirilyapti?" deb hayron bo'ladi.

Yana bir tuzoq:

python
from collections.abc import Iterable

class A:
    def __getitem__(self, i): ...

isinstance(A(), Iterable)       # False  ⚠️
list(A())                       # ✅ ishlaydi

isinstance va amaliyot mos kelmaydi — bu chalkash.

5. Qanday hal qilingan

Python muammoni olib tashlash o'rniga hujjatlash bilan hal qildi:

a) Rasmiy hujjatda ogohlantirish:

"Objects can also provide the older iteration protocol using __getitem__()... but this is deprecated in favor of __iter__()."

b) collections.abc aniq:

python
Iterable → faqat __iter__
Sequence → __getitem__ + __len__ (va __iter__ ni BERADI)

Sequence dan meros olsangiz — __iter__ bepul olasiz:

python
class A(Sequence):
    def __getitem__(self, i): ...
    def __len__(self): ...

isinstance(A(), Iterable)       # True  ✅

c) mypy tekshiradi:

python
def f(x: Iterable[int]): ...

class A:
    def __getitem__(self, i) -> int: ...

f(A())
# mypy: Argument 1 has incompatible type "A"; expected "Iterable[int]"

6. Boshqa tillar

Til Yondashuv
Python Ikki protokol (eski saqlangan)
Java Iterable<T> interfeysi (bitta)
C# IEnumerable<T> (bitta)
Ruby each metodi + Enumerable modul
JavaScript Symbol.iterator (ES6 da qo'shildi)
Rust IntoIterator trait
Go range — faqat o'rnatilgan turlar (1.23 gacha)

JavaScript ham shunday muammoni boshdan kechirdi:

javascript
// ES5 gacha:
for (var i = 0; i < arr.length; i++) { ... }

// ES6:
for (const x of iterable) { ... }       // Symbol.iterator

JavaScript eski usulni olib tashlamadi — ikkalasi ham ishlaydi.

Go 1.23 (2024) da:

go
// Oldin: faqat slice, map, chan
for i, v := range slice { ... }

// 1.23: range over func
for v := range mening_iteratorim { ... }

Go ham eskisini saqlab, yangisini qo'shdi.

7. Deprecation siyosati

Python nimani olib tashlaydi?

Turi Misol Natija
Xavfsizlik xavfi crypt moduli Olib tashlandi (3.13)
Buzilgan API asyncio.async Olib tashlandi
Kam ishlatiladigan modul imp, distutils Olib tashlandi
Ishlaydigan xatti-harakat eski iteratsiya Saqlanadi

Qoida: agar biror narsa ishlaydi va zarar keltirmaydi — u qoladi.

PEP 387 (Backwards Compatibility Policy) buni rasmiylashtirdi:

Xatti-harakatni o'zgartirish uchun kamida ikki versiya ogohlantirish kerak, va o'zgarish jiddiy sabab talab qiladi.

8. Shunga o'xshash misollar

Pythonda saqlanib qolgan boshqa "eski" narsalar:

python
# 1. % formatlash (f-string bo'lsa ham)
"%s" % x                        # ✅ hali ishlaydi

# 2. dict.has_key() — ❌ olib tashlangan (Python 3 da)

# 3. Eski uslub sinflar — ❌ olib tashlangan (Python 3 da)

# 4. __cmp__ — ❌ olib tashlangan

# 5. Eski iteratsiya — ✅ saqlanib qoldi

Naqsh: Python 3 ga o'tishda katta tozalash bo'ldi. Undan keyin — juda ehtiyotkorlik.

9. Amaliy tavsiya

python
# ⭐ 1. Yangi kodda __iter__ yozing
class A:
    def __iter__(self):
        yield from self._el

# ⭐ 2. Yoki ABC dan meros oling
class A(Sequence):
    def __getitem__(self, i): ...
    def __len__(self): ...
    # __iter__ bepul

# ⚠️ 3. __getitem__ satr kalitini oladi bo'lsa —
#      __iter__ ni ALBATTA yozing
class Lugat:
    def __getitem__(self, k): return self._d[k]
    def __iter__(self): return iter(self._d)    # ⭐ SHART

# ⭐ 4. mypy ishlating
def f(x: Iterable[int]): ...

10. Xulosa

Eski protokol saqlanadi, chunki:

  1. Orqaga moslik — Pythonning asosiy va'dasi
  2. Ishlaydigan kod buzilmasligi kerak — millionlab qator
  3. Foydasi kam — olib tashlash deyarli hech narsa bermaydi
  4. Python 3 travmasi — jamoa katta buzilishlarga tayyor emas
  5. Ba'zan tabiiy — hisoblanadigan ketma-ketliklar uchun

Umumiy dars: til dizaynida "toza" yechim har doim ham to'g'ri yechim emas. Pythonning muvaffaqiyati qisman shunda — u ishlaydigan kodni buzmaydi.

Guido van Rossum:

"Har bir eskirgan xususiyat — kimningdir ishlayotgan dasturi. Uni olib tashlash — o'sha odamning ishini buzish."

Nimani mustahkamlaydi: 2.2, 2.7-bo'limlar.


Xulosa

Bu darsda konteyner protokollarini o'rgandik.

Eng muhim uch fikr:

  1. Iteratsiya ikki yo'l bilan ishlaydi. iter(x) avval __iter__ ni qidiradi; topilmasa __getitem__ ni 0, 1, 2, ... bilan IndexError gacha chaqiradi (Python 2.0 dan qolgan eski protokol). Shuning uchun __getitem__ yozilgan sinf for bilan ishlaydi, lekin isinstance(x, Iterable) — False. Yangi kodda __iter__ ni aniq yozing, ayniqsa kalitlar satr bo'lsa (aks holda KeyError bilan buziladi).

  2. collections.abc — o'nlab metodni bepul beradi. Sequence dan meros olsangiz, __getitem__ va __len__ yozib, __contains__, __iter__, __reversed__, index, count ni olasiz. MutableSequence esa 5 metoddan append, pop, remove, extend, __iadd__ va boshqalarni yasaydi.

  3. __getitem__ uch holatni qayta ishlashi kerak. int (salbiy indeksni ham), slice (natija o'sha turda bo'lsin) va boshqa turlar uchun aniq TypeError. dict/list dan meros olish o'rniga UserDict/UserList ishlating — C da yozilgan metodlar sizning __setitem__ ingizni chaqirmaydi.

Keyingi darsda @property ni chuqur ko'ramiz: deskriptor protokoli, getter/setter/deleter va hisoblanadigan atributlarni to'g'ri loyihalash.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.14-dars: Dunder: konteyner protokoli — IlmHamroh