IlmHamroh
Python kursi/Ilgor til imkoniyatlari15/17-dars29 daqiqa
Mundarija (20)

10.15-dars: Python ma'lumot modeli

10-QISM — ILG'OR TIL IMKONIYATLARI · 15-dars


1. Kirish va motivatsiya

8-qismda bir necha dunder metodlarni alohida o'rgandik: __str__, __eq__, __add__, __len__, __getitem__. 10-qismda esa __iter__, __enter__, __call__, __get__ ga duch keldik. Endi bu metodlarni bitta tizim sifatida ko'rish vaqti.

Jamoa istalgan obyektni o'rab, har murojaatni jurnalga yozadigan proksi yozdi:

python
class Proksi:
    def __init__(self, asl):
        self._asl = asl

    def __getattr__(self, nom):
        jurnal.info(f"murojaat: {nom}")
        return getattr(self._asl, nom)

royxat = Proksi([3, 1, 2])
royxat.append(4)        # ✅ ishlaydi
royxat.__len__()        # ✅ 4
len(royxat)             # ❌ TypeError: object of type 'Proksi' has no len()
royxat + [5]            # ❌ TypeError: unsupported operand type(s) for +
for x in royxat: ...    # ❌ TypeError: 'Proksi' object is not iterable

royxat.__len__() ishlaydi, len(royxat) esa yo'q. Nega?

Chunki len(), +, for va boshqa sintaksis maxsus metodni obyektdan emas, uning turidan qidiradi — va __getattr__ ni ham, __getattribute__ ni ham chetlab o'tadi.

Python ma'lumot modeli — interpretator va obyektlar orasidagi shartnoma: qaysi sintaksis qaysi maxsus metodga aylanadi, metod qayerdan qidiriladi, metod bo'lmasa qanday zaxira yo'l bor. Bu shartnomani bilgan dasturchi yangi turni "o'rnatilgan tur kabi" ishlaydigan qila oladi — va proksi, mock, ORM kabi vositalardagi g'alati xatolarni tushunadi.

Bu darsda:

  • Obyekt = identifikatsiya + tur + qiymat; type va object munosabati
  • Sintaksis → maxsus metod xaritasi (protokollar guruhlari)
  • Maxsus metodlar turda qidiriladi — va bu nimani buzadi
  • Zaxira zanjirlari: in, iter, bool, reversed, !=, str, format
  • Protokolni None bilan o'chirish
  • collections.abc tekshiruvlari va haqiqiy xatti-harakat orasidagi farq

2. Nazariya — chuqur tushuntirish

2.1. Obyekt: identifikatsiya, tur, qiymat

Python'dagi hamma narsa — son, funksiya, klass, modul, kod obyekti, istisno — obyekt. Har obyektning uch xossasi bor:

Xossa Qanday olinadi O'zgaradimi
Identifikatsiya id(x), x is y Hech qachon (obyekt yashagan paytda)
Tur type(x) Deyarli hech qachon (__class__ ni faqat mos turlar orasida almashtirish mumkin)
Qiymat ==, atributlar, tarkib O'zgaruvchan turlarda — ha, o'zgarmaslarda — yo'q

Tur obyektning xatti-harakatini belgilaydi: qaysi amallar mumkin, qanday bajariladi. Shuning uchun maxsus metodlar turga yoziladi.

type va object:

python
type(5)            # int
type(int)          # type      — klass ham obyekt, uning turi type
type(type)         # type      — type o'zining turi
int.__bases__      # (object,)
type.__bases__     # (object,) — type ham object dan meros oladi
object.__bases__   # ()        — ierarxiya ildizi
isinstance(type, object), isinstance(object, type)   # True, True
  • object — barcha klasslarning bazasi (meros zanjiri)
  • type — barcha klasslarning turi (metaklass)

2.2. Sintaksis → maxsus metod xaritasi

Guruh Sintaksis / funksiya Maxsus metod
Hayot davri K(...) __new__, __init__ (10.16-dars)
obyekt yo'qolganda __del__
Ko'rinish repr(x), str(x), f"{x:spec}", bytes(x) __repr__, __str__, __format__, __bytes__
Taqqoslash ==, <, ... ; hash(x) __eq__, __lt__, ... ; __hash__
Rostlik if x, bool(x), and/or/not __bool__ → __len__
Atributlar x.a, x.a = 1, del x.a, dir(x) __getattribute__, __getattr__, __setattr__, __delattr__, __dir__
Deskriptorlar klass atributiga murojaat __get__, __set__, __delete__, __set_name__ (10.17-dars)
Chaqirish x(...) __call__
Konteyner len, x[k], x[k] = v, del x[k], in, iter, reversed __len__, __getitem__, __setitem__, __delitem__, __contains__, __iter__, __reversed__
operator.length_hint ; dict merosxo'rida yo'q kalit __length_hint__ ; __missing__
Iterator next(x) __next__
Sonlar + - * / // % ** @ & | ^ << >>, unar, abs, round __add__/__radd__/__iadd__, ...
int(), float(), indeks sifatida __int__, __float__, __index__
Kontekst with ; async with __enter__/__exit__ ; __aenter__/__aexit__
Asinxron await x, async for __await__, __aiter__, __anext__
Klass yaratish class K(B), K[int] __init_subclass__, __class_getitem__, __mro_entries__
isinstance, issubclass metaklassda __instancecheck__, __subclasscheck__
Nusxa va pickle copy.copy, copy.deepcopy, pickle __copy__, __deepcopy__, __reduce_ex__, __getstate__, __setstate__
Boshqalar os.fspath(x), match pozitsion naqsh, sys.getsizeof __fspath__, __match_args__, __sizeof__

2.3. Maxsus metodlar turda qidiriladi

len(x) taxminan type(x).__len__(x) — x.__len__() emas.

Oqibatlari:

Holat Natija
Nusxaga x.__len__ = lambda: 5 len(x) e'tibor bermaydi — TypeError
__getattribute__ yuklangan len(x) uni chaqirmaydi (x.__len__() esa chaqiradi)
__getattr__ asosidagi proksi len(proksi), proksi + 1, for ... in proksi ishlamaydi
Klass yaratilgach unga K.__len__ = ... qo'shish ishlaydi — tur o'zgardi

Nega shunday?

  1. Tezlik. CPython har tur uchun maxsus metodlarni tur slotlarida (tp_as_sequence, tp_as_number ...) saqlaydi. len(x) — slotdagi C ko'rsatkichni chaqirish, lug'at qidiruvi yo'q
  2. Metaklass muammosi. Agar repr(x) avval x.__repr__ ni qidirganda, repr(int) int.__repr__ ni — ya'ni int nusxalari uchun metodni — topib, self siz chaqirar edi. Turdan qidirish: repr(int) → type(int).__repr__(int) → type.__repr__(int)

Proksi uchun yechim: kerakli maxsus metodlarni klassga aniq yozish yoki klass yaratishda ularni avtomatik generatsiya qilish (Misol 2).

2.4. Zaxira zanjirlari

Maxsus metod bo'lmasa, Python ko'pincha eski yoki umumiyroq protokolga qaytadi:

Amal Tartib
bool(x) __bool__ → __len__() != 0 → True
x in y __contains__ → __iter__ bo'ylab == → __getitem__(0, 1, 2, ...) IndexError gacha
iter(x) __iter__ → __getitem__(0, 1, 2, ...)
reversed(x) __reversed__ → __len__ + __getitem__
x != y __ne__ → __eq__ natijasining inkori (NotImplemented bo'lmasa)
str(x) __str__ → __repr__
format(x, spec) __format__ → object.__format__: spec bo'sh bo'lsa str(x), aks holda TypeError
hash(x) __hash__; klassda __eq__ yozilib __hash__ yozilmasa → None (xeshlanmaydi)
a + b a.__add__(b) → b.__radd__(a) (merosxo'r ustuvorligi bilan, 8.13-dars)
copy.copy(x) __copy__ → __reduce_ex__(4)

__getitem__ asosidagi iteratsiya — eski ketma-ketlik protokoli. U ishlaydi, lekin collections.abc.Iterable uni tanimaydi 2.6-bob.

2.5. Protokolni o'chirish: = None

Meros qilingan yoki zaxira yo'l bilan ishlaydigan protokolni aniq o'chirish uchun maxsus metodni None ga tenglashtiring:

python
class Hisob:
    __hash__ = None          # xeshlanmaydi (o'zgaruvchan tur)

class Satrlar:
    def __getitem__(self, i): ...
    __iter__ = None          # iter() va for — TypeError; __getitem__ zaxirasi ham ishlamaydi

Bu collections.abc tekshiruvlariga ham ta'sir qiladi: __iter__ = None bo'lsa, isinstance(x, Iterable) — False.

2.6. collections.abc va haqiqiy xatti-harakat

isinstance(x, abc.Iterable) — __iter__ bor-yo'qligini tekshiradi (__subclasshook__). Haqiqiy iteratsiya esa __getitem__ zaxirasini ham qo'llaydi:

Klassda iter(x) ishlaydimi isinstance(x, Iterable)
__iter__
Faqat __getitem__
__getitem__ + __len__ (Sequence ham emas — ro'yxatdan o'tkazish kerak)
__iter__ = None

Qoidalar:

  • "Iteratsiya qilsa bo'ladimi?" degan savolga eng ishonchli javob — iter(x) ni chaqirib, TypeError ni ushlash
  • Sequence, Mapping kabi murakkab ABC lar faqat meros yoki register() bilan taniladi (ular __subclasshook__ ishlatmaydi)
  • Yangi turni yozganda ABC dan meros oling — mixin metodlarni (__contains__, __iter__, index, count ...) bepul olasiz va isinstance to'g'ri ishlaydi

3. Tez ma'lumotnoma

Obyekt

id(x) / x is y     identifikatsiya — o'zgarmaydi
type(x)            tur — xatti-harakat
== / tarkib        qiymat
type(type) is type    type.__bases__ == (object,)    object.__bases__ == ()

Turda qidirish

len(x)  →  type(x).__len__(x)       (x.__len__ emas)
nusxadagi dunder, __getattribute__, __getattr__ — chetlab o'tiladi
proksi: dunderlarni klassga yozing

Zaxiralar

bool:     __bool__ → __len__ → True
in:       __contains__ → __iter__ → __getitem__
iter:     __iter__ → __getitem__
reversed: __reversed__ → __len__ + __getitem__
!=:       __ne__ → not __eq__
str:      __str__ → __repr__
hash:     __eq__ yozilsa → None

O'chirish va tekshirish

python
__hash__ = None       __iter__ = None
try: iter(x)          # isinstance(x, Iterable) dan ishonchliroq
except TypeError: ...

4. Batafsil misollar

Misol 1 — Obyekt, tur va qiymat

python
"""Hamma narsa obyekt; identifikatsiya/tur/qiymat; type va object; __class__ ni almashtirish."""

import types


def korsat(nom, x):
    print(f"  {nom:18} tur={type(x).__name__:18} turining turi={type(type(x)).__name__}")


print("=== 1. Hamma narsa obyekt ===")


def funksiya():
    pass


class Klass:
    pass


for nom, x in [("5", 5), ("'matn'", "matn"), ("funksiya", funksiya), ("Klass", Klass),
               ("Klass()", Klass()), ("types moduli", types), ("funksiya.__code__", funksiya.__code__),
               ("ValueError", ValueError), ("None", None), ("type", type)]:
    korsat(nom, x)

print("\n=== 2. type va object ===")
print(f"  type(type) is type: {type(type) is type}")
print(f"  type.__bases__: {type.__bases__}, object.__bases__: {object.__bases__}")
print(f"  isinstance(type, object): {isinstance(type, object)}, isinstance(object, type): {isinstance(object, type)}")
print(f"  int.__mro__: {[k.__name__ for k in int.__mro__]}, bool.__mro__: {[k.__name__ for k in bool.__mro__]}")

print("\n=== 3. Identifikatsiya, tur, qiymat ===")
a = [1, 2]
b = a
c = [1, 2]
print(f"  a == c: {a == c}, a is c: {a is c}, a is b: {b is a}")
eski_id = id(a)
a.append(3)
print(f"  append dan keyin: id o'zgarmadi {id(a) == eski_id}, qiymat o'zgardi {a}, b ham: {b}")
s = "salom"
eski_id = id(s)
s += "!"
print(f"  satr += dan keyin: id o'zgardimi {id(s) != eski_id} — yangi obyekt")
t = (1, [2])
t[1].append(3)
print(f"  kortej o'zgarmas, lekin ichidagi ro'yxat o'zgaradi: {t}")

print("\n=== 4. Tur xatti-harakatni belgilaydi: __class__ ni almashtirish ===")


class Qoralama:
    def holat(self):
        return f"qoralama: {self.matn!r}"


class Chop:
    def holat(self):
        return f"chop etilgan: {self.matn!r}"


h = Qoralama()
h.matn = "Hisobot"
print(f"  {h.holat()}")
h.__class__ = Chop
print(f"  __class__ = Chop dan keyin: {h.holat()}, isinstance(h, Qoralama): {isinstance(h, Qoralama)}")
try:
    x = 5
    x.__class__ = float
except TypeError as e:
    print(f"  int uchun: TypeError: {e}")

Natijaning muhim qismi:

text
=== 1. Hamma narsa obyekt ===
  5                  tur=int                turining turi=type
  'matn'             tur=str                turining turi=type
  funksiya           tur=function           turining turi=type
  Klass              tur=type               turining turi=type
  Klass()            tur=Klass              turining turi=type
  types moduli       tur=module             turining turi=type
  funksiya.__code__  tur=code               turining turi=type
  ValueError         tur=type               turining turi=type
  None               tur=NoneType           turining turi=type
  type               tur=type               turining turi=type

=== 2. type va object ===
  type(type) is type: True
  type.__bases__: (<class 'object'>,), object.__bases__: ()
  isinstance(type, object): True, isinstance(object, type): True
  int.__mro__: ['int', 'object'], bool.__mro__: ['bool', 'int', 'object']

=== 3. Identifikatsiya, tur, qiymat ===
  a == c: True, a is c: False, a is b: True
  append dan keyin: id o'zgarmadi True, qiymat o'zgardi [1, 2, 3], b ham: [1, 2, 3]
  satr += dan keyin: id o'zgardimi True — yangi obyekt
  kortej o'zgarmas, lekin ichidagi ro'yxat o'zgaradi: (1, [2, 3])

=== 4. Tur xatti-harakatni belgilaydi: __class__ ni almashtirish ===
  qoralama: 'Hisobot'
  __class__ = Chop dan keyin: chop etilgan: 'Hisobot', isinstance(h, Qoralama): False
  int uchun: TypeError: __class__ assignment only supported for mutable types or ModuleType subclasses

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Maxsus metodlar turda qidiriladi

python
"""Nusxadagi dunder e'tiborsiz; __getattribute__ chetlab o'tiladi; __getattr__ proksisi va uni tuzatish."""

print("=== 1. Nusxaga qo'shilgan __len__ ===")


class Bosh:
    pass


b = Bosh()
b.__len__ = lambda: 5
print(f"  b.__len__(): {b.__len__()}")
try:
    len(b)
except TypeError as e:
    print(f"  len(b): TypeError: {e}")
Bosh.__len__ = lambda self: 7
print(f"  klassga qo'shilgach len(b): {len(b)}   (nusxadagi 5 emas)")

print("\n=== 2. __getattribute__ chetlab o'tiladi ===")
chaqiruvlar = []


class Kuzatilgan:
    def __getattribute__(self, nom):
        chaqiruvlar.append(nom)
        return object.__getattribute__(self, nom)

    def __len__(self):
        return 3

    def __repr__(self):
        return "Kuzatilgan()"


k = Kuzatilgan()
len(k), repr(k), bool(k)
print(f"  len, repr, bool dan keyin __getattribute__ chaqiruvlari: {chaqiruvlar}")
k.__len__()
k.__repr__()
print(f"  aniq k.__len__(), k.__repr__() dan keyin: {chaqiruvlar}")

print("\n=== 3. ⚠️ __getattr__ proksisi ===")


class Proksi:
    def __init__(self, asl):
        object.__setattr__(self, "_asl", asl)
        object.__setattr__(self, "jurnal", [])

    def __getattr__(self, nom):
        self.jurnal.append(nom)
        return getattr(self._asl, nom)


p = Proksi([3, 1, 2])
p.append(4)
print(f"  p.append(4), p.__len__() = {p.__len__()}, jurnal: {p.jurnal}")
for tavsif, amal in [("len(p)", lambda: len(p)), ("p[0]", lambda: p[0]), ("p + [5]", lambda: p + [5]),
                     ("list(p)", lambda: list(p)), ("2 in p", lambda: 2 in p), ("str(p)", lambda: str(p))]:
    try:
        natija = amal()
        print(f"  {tavsif:10} → {natija if tavsif != 'str(p)' else natija.split(' object')[0] + ' object ...>'}")
    except TypeError as e:
        print(f"  {tavsif:10} → TypeError: {e}")

print("\n=== 4. ✅ Tuzatish: maxsus metodlarni klassga generatsiya qilish ===")
UZATILADIGAN = ["__len__", "__getitem__", "__setitem__", "__iter__", "__contains__",
                "__add__", "__radd__", "__eq__", "__repr__", "__str__", "__bool__"]


def proksi_klassi(asl_tur):
    def uzat(nom):
        def metod(self, *args):
            self.jurnal.append(nom)
            return getattr(self._asl, nom)(*args)
        metod.__name__ = nom
        return metod

    lugat = {nom: uzat(nom) for nom in UZATILADIGAN if hasattr(asl_tur, nom)}
    lugat["__hash__"] = None
    return type(f"{asl_tur.__name__.title()}Proksi", (Proksi,), lugat)


RoyxatProksi = proksi_klassi(list)
p = RoyxatProksi([3, 1, 2])
print(f"  len(p)={len(p)}, p[0]={p[0]}, p + [5]={p + [5]}, list(p)={list(p)}, 2 in p={2 in p}, str(p)={p}")
print(f"  jurnal: {p.jurnal}")
print(f"  klass nomi: {type(p).__name__}, oddiy metod hali ham __getattr__ orqali: {p.count(1)}, jurnal oxiri: {p.jurnal[-1]}")

print("\n=== 5. Nega turda: metaklass muammosi ===")
print(f"  repr(int) = {repr(int)!r}  — type.__repr__(int) chaqirildi")
print(f"  int.__repr__(5) = {int.__repr__(5)!r}  — int nusxalari uchun metod")
try:
    int.__repr__()
except TypeError as e:
    print(f"  agar repr(int) → int.__repr__() bo'lganda: TypeError: {e}")

Natijaning muhim qismi:

text
=== 1. Nusxaga qo'shilgan __len__ ===
  b.__len__(): 5
  len(b): TypeError: object of type 'Bosh' has no len()
  klassga qo'shilgach len(b): 7   (nusxadagi 5 emas)

=== 2. __getattribute__ chetlab o'tiladi ===
  len, repr, bool dan keyin __getattribute__ chaqiruvlari: []
  aniq k.__len__(), k.__repr__() dan keyin: ['__len__', '__repr__']

=== 3. ⚠️ __getattr__ proksisi ===
  p.append(4), p.__len__() = 4, jurnal: ['append', '__len__']
  len(p)     → TypeError: object of type 'Proksi' has no len()
  p[0]       → TypeError: 'Proksi' object is not subscriptable
  p + [5]    → TypeError: unsupported operand type(s) for +: 'Proksi' and 'list'
  list(p)    → TypeError: 'Proksi' object is not iterable
  2 in p     → TypeError: argument of type 'Proksi' is not a container or iterable
  str(p)     → <__main__.Proksi object ...>

=== 4. ✅ Tuzatish: maxsus metodlarni klassga generatsiya qilish ===
  len(p)=3, p[0]=3, p + [5]=[3, 1, 2, 5], list(p)=[3, 1, 2], 2 in p=True, str(p)=[3, 1, 2]
  jurnal: ['__len__', '__getitem__', '__add__', '__iter__', '__len__', '__contains__', '__str__']
  klass nomi: ListProksi, oddiy metod hali ham __getattr__ orqali: 1, jurnal oxiri: count

=== 5. Nega turda: metaklass muammosi ===
  repr(int) = "<class 'int'>"  — type.__repr__(int) chaqirildi
  int.__repr__(5) = '5'  — int nusxalari uchun metod
  agar repr(int) → int.__repr__() bo'lganda: TypeError: descriptor '__repr__' of 'int' object needs an argument

Nima ko'rsatdi: 2.3-bo'lim.

Misol 3 — Zaxira zanjirlari va protokolni o'chirish

python
"""__getitem__ bilan iteratsiya va in; __len__ bilan bool va reversed; != va str zaxiralari; = None; abc farqlari."""

import collections.abc as abc


class Hafta:
    """Faqat __getitem__ — eski ketma-ketlik protokoli."""
    kunlar = ["Dush", "Sesh", "Chor", "Pay", "Juma", "Shan", "Yak"]

    def __getitem__(self, i):
        print(f"      __getitem__({i})", end="")
        return self.kunlar[i]


print("=== 1. Faqat __getitem__ ===")
h = Hafta()
print(f"  list(h):")
natija = list(h)
print(f"\n    → {natija}")
print(f"  'Chor' in h:")
bor = "Chor" in h
print(f"\n    → {bor}   (Chor topilgach to'xtadi)")
try:
    reversed(h)
except TypeError as e:
    print(f"  reversed(h): TypeError: {e}")
print(f"  bool(h): {bool(h)}   (__bool__ ham __len__ ham yo'q → True)")


class HaftaLen(Hafta):
    def __len__(self):
        return len(self.kunlar)


print("\n=== 2. __getitem__ + __len__ ===")
hl = HaftaLen()
print("  reversed:", end="")
teskari = list(reversed(hl))
print(f"\n    → {teskari[:3]} ...")


class Bosh(Hafta):
    kunlar = []

    def __len__(self):
        return 0


print(f"  bo'sh: bool={bool(Bosh())}   (__len__ = 0)")

print("\n=== 3. !=, str, format, hash zaxiralari ===")


class Nuqta:
    def __init__(self, x):
        self.x = x

    def __eq__(self, boshqa):
        print(f"      __eq__ chaqirildi", end="")
        return isinstance(boshqa, Nuqta) and self.x == boshqa.x

    def __repr__(self):
        return f"Nuqta({self.x})"


print(f"  Nuqta(1) != Nuqta(2):", end="")
farq = Nuqta(1) != Nuqta(2)
print(f" → {farq}")
print(f"  str(Nuqta(1)) = {str(Nuqta(1))!r} (__str__ yo'q → __repr__), f'{{Nuqta(1)}}' = {Nuqta(1)}")
try:
    format(Nuqta(1), ">10")
except TypeError as e:
    print(f"  format(Nuqta(1), '>10'): TypeError: {e}")
print(f"  Nuqta.__hash__: {Nuqta.__hash__}   (__eq__ yozildi → xeshlanmaydi)")

print("\n=== 4. Protokolni None bilan o'chirish ===")


class YopiqHafta(Hafta):
    __iter__ = None


for tavsif, amal in [("iter()", lambda: iter(YopiqHafta())), ("'Dush' in", lambda: "Dush" in YopiqHafta()),
                     ("list()", lambda: list(YopiqHafta()))]:
    try:
        amal()
        print(f"  {tavsif}: ishladi")
    except TypeError as e:
        print(f"  {tavsif}: TypeError: {e}")
print(f"  lekin indeks bilan murojaat qoladi:", end="")
kun = YopiqHafta()[0]
print(f" → {kun}")

print("\n=== 5. collections.abc va haqiqiy xatti-harakat ===")


def iteratsiya_qilsa_boladimi(x):
    try:
        iter(x)
        return True
    except TypeError:
        return False


class Toliq(abc.Sequence):
    def __init__(self, d):
        self.d = d

    def __getitem__(self, i):
        return self.d[i]

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


print(f"  {'tur':12} {'iter() ishlaydi':16} {'Iterable':9} {'Sequence':9}")
for nom, x in [("list", [1]), ("Hafta", Hafta()), ("HaftaLen", HaftaLen()), ("YopiqHafta", YopiqHafta()),
               ("Toliq", Toliq([1, 2, 2]))]:
    import io, contextlib
    with contextlib.redirect_stdout(io.StringIO()):
        ishlaydi = iteratsiya_qilsa_boladimi(x)
    print(f"  {nom:12} {str(ishlaydi):16} {str(isinstance(x, abc.Iterable)):9} {str(isinstance(x, abc.Sequence)):9}")
t = Toliq([1, 2, 2])
print(f"  Toliq — ABC dan bepul: count(2)={t.count(2)}, index(2)={t.index(2)}, 2 in t={2 in t}, reversed={list(reversed(t))}")

Natijaning muhim qismi:

text
=== 1. Faqat __getitem__ ===
  list(h):
      __getitem__(0)      __getitem__(1)      __getitem__(2)      __getitem__(3)      __getitem__(4)      __getitem__(5)      __getitem__(6)      __getitem__(7)
    → ['Dush', 'Sesh', 'Chor', 'Pay', 'Juma', 'Shan', 'Yak']
  'Chor' in h:
      __getitem__(0)      __getitem__(1)      __getitem__(2)
    → True   (Chor topilgach to'xtadi)
  reversed(h): TypeError: object of type 'Hafta' has no len()
  bool(h): True   (__bool__ ham __len__ ham yo'q → True)

=== 2. __getitem__ + __len__ ===
  reversed:      __getitem__(6)      __getitem__(5)      __getitem__(4)      __getitem__(3)      __getitem__(2)      __getitem__(1)      __getitem__(0)
    → ['Yak', 'Shan', 'Juma'] ...
  bo'sh: bool=False   (__len__ = 0)

=== 3. !=, str, format, hash zaxiralari ===
  Nuqta(1) != Nuqta(2):      __eq__ chaqirildi → True
  str(Nuqta(1)) = 'Nuqta(1)' (__str__ yo'q → __repr__), f'{Nuqta(1)}' = Nuqta(1)
  format(Nuqta(1), '>10'): TypeError: unsupported format string passed to Nuqta.__format__
  Nuqta.__hash__: None   (__eq__ yozildi → xeshlanmaydi)

=== 4. Protokolni None bilan o'chirish ===
  iter(): TypeError: 'YopiqHafta' object is not iterable
  'Dush' in: TypeError: argument of type 'YopiqHafta' is not a container or iterable
  list(): TypeError: 'YopiqHafta' object is not iterable
  lekin indeks bilan murojaat qoladi:      __getitem__(0) → Dush

=== 5. collections.abc va haqiqiy xatti-harakat ===
  tur          iter() ishlaydi  Iterable  Sequence
  list         True             True      True
  Hafta        True             False     False
  HaftaLen     True             False     False
  YopiqHafta   False            False     False
  Toliq        True             True      True
  Toliq — ABC dan bepul: count(2)=2, index(2)=1, 2 in t=True, reversed=[2, 2, 1]

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

Misol 4 — Amaliy: protokollar xaritasi

Istalgan obyekt uchun "u qaysi protokollarni qo'llab-quvvatlaydi va qaysi yo'l bilan" degan hisobot yasaydigan vosita. Tur lug'atlari MRO bo'ylab tekshiriladi (nusxa emas — 2.3), zaxira yo'llar alohida belgilanadi.

python
"""Tur bo'yicha protokollarni aniqlash (to'g'ridan-to'g'ri / zaxira / o'chirilgan); o'rnatilgan va o'z turlarimiz."""

import collections
import collections.abc as abc
from dataclasses import dataclass
from fractions import Fraction

TOLIQ_YOQ = object()


def turda(tur, nom):
    """MRO bo'ylab klass lug'atlaridan qidiradi (object dagisini alohida belgilaydi)."""
    for klass in tur.__mro__:
        if nom in vars(klass):
            qiymat = vars(klass)[nom]
            if qiymat is None:
                return "o'chirilgan"
            return "object" if klass is object else "bor"
    return TOLIQ_YOQ


def protokollar(x):
    tur = type(x)
    t = lambda nom: turda(tur, nom)
    natija = {}

    def belgila(nom, asosiy, *zaxiralar):
        holat = t(asosiy)
        if holat == "bor":
            natija[nom] = "✅"
            return
        if holat == "o'chirilgan":
            natija[nom] = "⛔"
            return
        for belgi, shart in zaxiralar:
            if shart():
                natija[nom] = f"↩ {belgi}"
                return
        natija[nom] = "—"

    belgila("iter", "__iter__", ("__getitem__", lambda: t("__getitem__") == "bor"))
    belgila("in", "__contains__", ("__iter__", lambda: t("__iter__") == "bor"),
            ("__getitem__", lambda: t("__getitem__") == "bor" and t("__iter__") is TOLIQ_YOQ))
    belgila("len", "__len__")
    belgila("bool", "__bool__", ("__len__", lambda: t("__len__") == "bor"), ("sukut", lambda: True))
    belgila("reversed", "__reversed__",
            ("len+getitem", lambda: t("__len__") == "bor" and t("__getitem__") == "bor"))
    belgila("hash", "__hash__", ("object", lambda: t("__hash__") == "object"))
    belgila("str", "__str__", ("__repr__", lambda: t("__repr__") == "bor"))
    belgila("chaqirish", "__call__")
    belgila("with", "__enter__")
    belgila("+", "__add__")
    return natija


@dataclass
class Talaba:
    ism: str
    ball: int = 0


@dataclass(frozen=True, order=True)
class Versiya:
    katta: int
    kichik: int


class Hafta:
    def __getitem__(self, i):
        return ["Dush", "Sesh"][i]


class Tovar:
    __hash__ = None
    __iter__ = None

    def __getitem__(self, i):
        return i


OBYEKTLAR = [
    ("list", [1]), ("tuple", (1,)), ("dict", {}), ("str", "a"), ("set", set()),
    ("generator", (x for x in [])), ("Counter", collections.Counter()), ("Fraction", Fraction(1, 2)),
    ("open fayl", open(__file__) if "__file__" in globals() else __import__("io").StringIO()),
    ("funksiya", len), ("Talaba", Talaba("A")), ("Versiya", Versiya(1, 0)),
    ("Hafta", Hafta()), ("Tovar", Tovar()),
]

ustunlar = list(protokollar([]).keys())
print("=== Protokollar xaritasi ===")
print("  ✅ to'g'ridan-to'g'ri   ↩ zaxira orqali   ⛔ = None bilan o'chirilgan   — yo'q\n")
print(f"  {'':11}" + "".join(f"{u:>15}" for u in ustunlar))
for nom, x in OBYEKTLAR:
    p = protokollar(x)
    print(f"  {nom:11}" + "".join(f"{p[u]:>15}" for u in ustunlar))

print("\n=== Xarita haqiqatga mosmi — tekshiruv ===")
tekshiruvlar = {
    "iter": lambda x: iter(x),
    "hash": lambda x: hash(x),
    "bool": lambda x: bool(x),
    "reversed": lambda x: reversed(x),
}
nomuvofiq = []
for nom, x in OBYEKTLAR:
    p = protokollar(x)
    for protokol, amal in tekshiruvlar.items():
        kutilgan = p[protokol] not in ("—", "⛔")
        try:
            amal(x)
            haqiqiy = True
        except (TypeError, ValueError):
            haqiqiy = False
        if kutilgan != haqiqiy:
            nomuvofiq.append((nom, protokol, p[protokol], haqiqiy))
HECH_BIRI = "yo'q — xarita haqiqatga mos"
print(f"  {len(OBYEKTLAR)} obyekt × {len(tekshiruvlar)} amal, nomuvofiqliklar: {nomuvofiq or HECH_BIRI}")
print(f"  dict reversed: {protokollar({})['reversed']} — Python 3.8 dan dict tartibni teskari aylanadi")
print(f"  Talaba hash: {protokollar(Talaba('A'))['hash']} — dataclass eq=True, frozen=False → __hash__ = None")
for _, x in OBYEKTLAR:
    if hasattr(x, "close") and not isinstance(x, abc.Generator):
        x.close()

Natijaning muhim qismi:

text
=== Protokollar xaritasi ===
  ✅ to'g'ridan-to'g'ri   ↩ zaxira orqali   ⛔ = None bilan o'chirilgan   — yo'q

                        iter             in            len           bool       reversed           hash            str      chaqirish           with              +
  list                     ✅              ✅              ✅      ↩ __len__              ✅              ⛔     ↩ __repr__              —              —              ✅
  tuple                    ✅              ✅              ✅      ↩ __len__  ↩ len+getitem              ✅     ↩ __repr__              —              —              ✅
  dict                     ✅              ✅              ✅      ↩ __len__              ✅              ⛔     ↩ __repr__              —              —              —
  str                      ✅              ✅              ✅      ↩ __len__  ↩ len+getitem              ✅              ✅              —              —              ✅
  set                      ✅              ✅              ✅      ↩ __len__              —              ⛔     ↩ __repr__              —              —              —
  generator                ✅     ↩ __iter__              —        ↩ sukut              —       ↩ object     ↩ __repr__              —              —              —
  Counter                  ✅              ✅              ✅      ↩ __len__              ✅              ⛔     ↩ __repr__              —              —              ✅
  Fraction                 —              —              —              ✅              —              ✅              ✅              —              —              ✅
  open fayl                ✅     ↩ __iter__              —        ↩ sukut              —       ↩ object     ↩ __repr__              —              ✅              —
  funksiya                 —              —              —        ↩ sukut              —              ✅     ↩ __repr__              ✅              —              —
  Talaba                   —              —              —        ↩ sukut              —              ⛔     ↩ __repr__              —              —              —
  Versiya                  —              —              —        ↩ sukut              —              ✅     ↩ __repr__              —              —              —
  Hafta        ↩ __getitem__  ↩ __getitem__              —        ↩ sukut              —       ↩ object              —              —              —              —
  Tovar                    ⛔              —              —        ↩ sukut              —              ⛔              —              —              —              —

=== Xarita haqiqatga mosmi — tekshiruv ===
  14 obyekt × 4 amal, nomuvofiqliklar: yo'q — xarita haqiqatga mos
  dict reversed: ✅ — Python 3.8 dan dict tartibni teskari aylanadi
  Talaba hash: ⛔ — dataclass eq=True, frozen=False → __hash__ = None

Nima ko'rsatdi: 2.2–2.6-bo'limlar — protokollarni turning MRO lug'atlaridan o'qish interpretator bilan bir xil natija beradi; dataclass va __hash__ = None, __iter__ = None kabi o'chirishlar ham shu xaritada ko'rinadi.


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

Noto'g'ri fikr To'g'risi
"len(x) — x.__len__() ning qisqa yozuvi" type(x).__len__(x) — nusxa, __getattr__ va __getattribute__ chetlab o'tiladi
"__getattr__ bilan istalgan obyektni to'liq proksilash mumkin" Maxsus metodlar klassga yozilishi kerak
"Klasslar va funksiyalar — maxsus narsalar, obyekt emas" Hamma narsa obyekt: identifikatsiya, tur, qiymat
"type ham, object ham ierarxiya ildizi" object — meros ildizi, type — tur ildizi; bir-birining nusxasi va merosxo'ri
"__iter__ bo'lmasa, for ishlamaydi" __getitem__ zaxirasi bor
"isinstance(x, Iterable) — iteratsiya mumkinligining aniq tekshiruvi" __getitem__ asosidagi turlarni tanimaydi — iter(x) ishonchliroq
"__eq__ yozsam, hash o'zgarmaydi" __hash__ avtomatik None bo'ladi
"Merosdagi metodni o'chirib bo'lmaydi" __hash__ = None, __iter__ = None — aniq o'chirish

6. Keng tarqalgan xatolar va yechimlari

1. Nusxaga maxsus metod qo'shish

python
obj.__len__ = lambda: 10               # ❌ len(obj) e'tibor bermaydi

type(obj).__len__ = lambda self: 10    # ✅ (lekin barcha nusxalarga ta'sir qiladi)

2. __getattr__ asosidagi proksi

python
class Proksi:
    def __getattr__(self, nom):
        return getattr(self._asl, nom)
len(Proksi([1]))                       # ❌ TypeError

Kerakli dunderlarni klassga yozing yoki generatsiya qiling (Misol 2); tayyor yechimlar: unittest.mock.MagicMock, wrapt.ObjectProxy.

3. isinstance(x, Iterable) ga tayanish

python
if isinstance(x, abc.Iterable):        # ❌ __getitem__ turlarini o'tkazib yuboradi
    ...

try:
    it = iter(x)                       # ✅
except TypeError:
    ...

4. __eq__ yozib, xeshni unutish

python
class Nuqta:
    def __eq__(self, b): ...
{Nuqta(1)}                             # ❌ TypeError: unhashable type

    def __hash__(self): return hash(self.x)   # ✅ (yoki o'zgaruvchan bo'lsa ataylab None)

5. __str__ va __repr__ ni aralashtirish

python
class Pul:
    def __str__(self): return "150 000 so'm"   # ❌ ro'yxatda, debuggerda <Pul object at 0x...>

    def __repr__(self): return "Pul('150000')" # ✅ avval __repr__ — __str__ unga qaytadi

6. __bool__ o'rniga __len__ ning tasodifiy ta'siri

python
class Savat:
    def __len__(self): return len(self.mahsulotlar)
if savat:                              # ⚠️ bo'sh savat — False; "savat obyekti bormi" degan ma'noda emas

Mavjudlikni if savat is not None: bilan tekshiring.

7. Klass metodini klassdan chaqirib, self ni unutish

python
int.__repr__()                         # ❌ TypeError — bu int NUSXALARI uchun metod
type.__repr__(int)                     # ✅ klass uchun — repr(int)

8. O'chirilishi kerak bo'lgan protokolni ochiq qoldirish

python
class Jadval(dict):                    # o'zgaruvchan, lekin...
    def __eq__(self, b): ...           # __hash__ = None avtomatik ✅

class Hisob:
    def __getitem__(self, i): ...      # ⚠️ for x in hisob — 0, 1, 2... indekslar bilan chaqiriladi
    __iter__ = None                    # ✅ ataylab taqiqlash

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 8.11–8.14-darslar (o'tilgan): ko'rinish, taqqoslash, arifmetika, konteyner protokollari
  • 10.1–10.7-darslar (o'tilgan): iterator va kontekst protokollari
  • 10.14-dars (o'tilgan): operator hal qilish
  • 10.16-dars: __new__ va __init__ — hayot davri protokoli
  • 10.17-dars: deskriptorlar — atributga murojaat protokoli
  • 12-qism: CPython ichki tuzilishi — PyObject va tur slotlari
  • 14-qism: asinxron protokollar: __await__, __aiter__, __aenter__
  • 17-qism: unittest.mock.MagicMock — nega maxsus metodlarni ham qo'llab-quvvatlaydi
  • 13-qism: typing.Protocol — protokollarning statik tekshiruvi

8. Eng yaxshi amaliyotlar

  1. Yangi tur — mos protokol to'plami. Konteyner bo'lsa __len__, __getitem__, __iter__, __contains__; qiymat bo'lsa __eq__, __hash__, __repr__.

  2. collections.abc dan meros oling — mixin metodlar bepul, isinstance to'g'ri.

  3. Maxsus metodlarni klassga yozing, nusxaga yoki __getattr__ ga tayanmang.

  4. Har turga __repr__ — __str__ ixtiyoriy.

  5. Zaxira yo'llarni biling — lekin yangi kodda asosiy protokolni aniq yozing (__iter__, __bool__).

  6. Keraksiz meros qilingan protokolni = None bilan o'chiring.

  7. "Qo'llab-quvvatlaydimi?" savoliga amalni sinab javob bering — iter(x), hash(x) — ABC tekshiruvi emas.

  8. Proksi va mock kutubxonalaridan foydalaning — maxsus metodlarni o'zingiz qo'lda uzatishdan oldin.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
import collections.abc as abc

class G:
    def __getitem__(self, i):
        if i >= 3:
            raise IndexError
        return i * 10

class P:
    def __init__(self, t):
        self._t = t
    def __getattr__(self, n):
        return getattr(self._t, n)

class E:
    def __eq__(self, o):
        return True
1.  print(list(G()))
2.  print(20 in G(), 5 in G())
3.  print(isinstance(G(), abc.Iterable))
4.  p = P([1, 2, 3])
    print(p.__len__(), p.count(2))
5.  try:
        len(P([1]))
    except TypeError:
        print("TypeError")
6.  print(E() == 1, E() != 1)
7.  print(E.__hash__)
8.  class K:
        pass
    k = K()
    k.__len__ = lambda: 7
    print(hasattr(k, "__len__"), k.__len__())
9.  class B:
        def __len__(self):
            return 0
    print(bool(B()), "bor" if B() else "yo'q")
10. print(type(type) is type, isinstance(type, object), issubclass(type, object))
11. class S:
        def __repr__(self):
            return "S!"
    print(str(S()), f"{S()}")
12. class Y:
        __iter__ = None
        def __getitem__(self, i):
            return i
    try:
        iter(Y())
    except TypeError:
        print("TypeError")
Javoblar
  1. [0, 10, 20] — __getitem__ zaxirasi, IndexError da to'xtaydi
  2. True False
  3. False — Iterable faqat __iter__ ni tekshiradi
  4. 3 1 — aniq murojaatlar __getattr__ orqali o'tadi
  5. TypeError — len() turda qidiradi
  6. True False — __ne__ __eq__ ning inkori
  7. None — __eq__ yozilgani uchun
  8. True 7 — atribut bor, lekin len(k) uni ishlatmaydi
  9. False yo'q — __len__ 0
  10. True True True
  11. S! S! — __str__ yo'q → __repr__, format → str
  12. TypeError — __iter__ = None zaxirani ham o'chiradi

Vazifa 2: Xatolarni tuzating

python
1.  class Lazy:
        def __init__(self, yuklovchi):
            self._yuklovchi = yuklovchi
            self._obyekt = None
        def __getattr__(self, nom):
            if self._obyekt is None:
                self._obyekt = self._yuklovchi()
            return getattr(self._obyekt, nom)
    royxat = Lazy(lambda: [1, 2, 3])
    print(len(royxat))

2.  def iteratsiya_qilinadimi(x):
        return isinstance(x, collections.abc.Iterable)

3.  class Foydalanuvchi:
        def __init__(self, id):
            self.id = id
        def __eq__(self, b):
            return self.id == b.id
    faollar = {Foydalanuvchi(1), Foydalanuvchi(2)}

4.  class Matn:
        def __init__(self, s):
            self.s = s
        def __str__(self):
            return self.s
    print([Matn("a"), Matn("b")])

5.  obj = Sozlama()
    obj.__contains__ = lambda k: k in obj.malumot
    print("til" in obj)
Javoblar
python
1.  class Lazy:
        def __init__(self, yuklovchi):
            object.__setattr__(self, "_yuklovchi", yuklovchi)
            object.__setattr__(self, "_obyekt", None)
        def _ol(self):
            if self._obyekt is None:
                object.__setattr__(self, "_obyekt", self._yuklovchi())
            return self._obyekt
        def __getattr__(self, nom):
            return getattr(self._ol(), nom)
        # maxsus metodlar klassda — len() turda qidiradi
        def __len__(self): return len(self._ol())
        def __iter__(self): return iter(self._ol())
        def __getitem__(self, i): return self._ol()[i]
        def __repr__(self): return f"Lazy({self._ol()!r})"

2.  def iteratsiya_qilinadimi(x):
        try:
            iter(x)
            return True
        except TypeError:
            return False

3.  class Foydalanuvchi:
        def __init__(self, id):
            self.id = id
        def __eq__(self, b):
            return self.id == b.id if isinstance(b, Foydalanuvchi) else NotImplemented
        def __hash__(self):             # __eq__ yozilgani uchun kerak
            return hash(self.id)

4.  class Matn:
        def __init__(self, s):
            self.s = s
        def __repr__(self):             # ro'yxat elementlarni repr bilan ko'rsatadi
            return f"Matn({self.s!r})"
        def __str__(self):
            return self.s

5.  class Sozlama:
        def __contains__(self, k):      # klassda, nusxada emas
            return k in self.malumot

Vazifa 3: Tekshiruvchi protokol auditori

Misol 4 ni kengaytiring:

  1. Barcha 2.2-jadvaldagi guruhlar uchun protokollarni aniqlasin (with, async with, await, copy, pickle, format spec, __index__)
  2. Har protokolni haqiqiy amal bilan ham tekshirib, xarita bilan solishtirsin
  3. "Shubhali" holatlarni belgilasin: __eq__ bor, __hash__ yo'q; __getitem__ bor, __iter__ yo'q; __str__ bor, __repr__ yo'q; __lt__ bor, __le__ yo'q va total_ordering ishlatilmagan
  4. Standart kutubxonadagi 20 ta turni tekshirib, qiziq topilmalarni yozing

Vazifa 4: To'liq proksi

ToliqProksi(asl) yozing:

  1. Barcha maxsus metodlarni (list, dict, int, str ning birlashmasi) turga uzatsin — asl turiga qarab kerakli klassni yaratib keshlasin
  2. Har murojaat va chaqiruvni vaqt bilan jurnalga yozsin
  3. isinstance(proksi, list) — True bo'lishi uchun __class__ xossasini yuklang va nima uchun bu ishlashini tushuntiring
  4. pickle, copy.deepcopy va == proksi orqali asl obyekt bilan qanday ishlashini tekshiring

Vazifa 5: O'z ketma-ketlik turingiz

HalqaBufer(sigim) — oxirgi sigim ta elementni saqlaydigan tur:

  1. collections.abc.MutableSequence dan meros olib, faqat majburiy abstrakt metodlarni yozing
  2. ABC dan bepul kelgan metodlarni (append, extend, pop, remove, __iadd__, index, count, __contains__, __reversed__) ro'yxatlab, har birini sinang
  3. Salbiy indekslar va kesmalar (slice) ni qo'llab-quvvatlang
  4. __eq__ (boshqa ketma-ketliklar bilan), __repr__ va __hash__ = None
  5. Tezlik: ABC dan kelgan __contains__ va o'zingiz yozgan versiyani timeit bilan solishtiring

Vazifa 6: Rostlik auditi

Loyihangizdagi (yoki kursdagi) barcha klasslar uchun:

  1. if obyekt: qayerda ishlatilganini ast bilan toping
  2. Shu klasslarda __bool__ yoki __len__ bor-yo'qligini aniqlang
  3. __len__ bor, lekin __bool__ yo'q klasslar uchun "bo'sh konteyner yolg'on" mantiqi kutilganmi — tekshiruv ro'yxatini yozing
  4. if x: ni if x is not None: ga almashtirish kerak bo'lgan joylarni belgilang

Vazifa 7: O'ylash

Python protokollarni dunder metodlar (__len__, __iter__) orqali belgilaydi, Java aniq interfeyslar (implements Iterable), Go esa struktural interfeyslar (metodlar mos kelsa — avtomatik) orqali. Nega Python len(x) ni x.len() metodi emas, alohida funksiya qilgan? Har yondashuv kutubxona yozuvchi va foydalanuvchi uchun nimani beradi?

Javob

Qisqa javob: Python protokollari — yashirin (struktural) shartnoma: kerakli dunder metod bor bo'lsa, obyekt protokolni qo'llab-quvvatlaydi, hech narsani e'lon qilish shart emas. Java — aniq (nominal): tur interfeysni e'lon qilishi kerak. Go — kompilyatsiya vaqtida tekshiriladigan struktural interfeys. len(x) alohida funksiya bo'lgani uchun interpretator umumiy amal ustidan nazoratni saqlaydi: turda qidirish, tezkor slot, natija turini tekshirish, zaxira yo'llar va yagona nom.

1. Python: dunder protokollar

python
class Savat:
    def __len__(self): return 3
len(Savat())            # hech qanday "implements" yo'q
  • Yozuvchi uchun oson — kerakli metodni yozish yetadi; eski turlarga ham protokol qo'shish mumkin
  • Duck typing: funksiya "ro'yxat"ni emas, "len va iteratsiyasi bor narsani" qabul qiladi
  • Shartnoma yashirin — metod nomida xato (__lenght__) jim o'tadi
  • Statik tekshiruv uchun qo'shimcha vosita kerak: collections.abc (ish vaqti), typing.Protocol (PEP 544, statik struktural tiplar)

2. Nega len(x), x.len() emas

Python rasmiy dizayn FAQ'ida bu savolga javob bor. Asosiy dalillar:

  • O'qilishi: len(x) "bu uzunlikni so'rash amali" ekanini darhol ko'rsatadi, x.len() esa "bu turning biror metodi" — nomi length, size, count bo'lishi mumkin edi. Umumiy amallar uchun yagona nom
  • Nazorat: len() natija manfiy bo'lmagan butun son ekanini tekshiradi (__len__ -1 qaytarsa — ValueError), iter() natija iterator ekanini, hash() — butun son ekanini
  • Zaxiralar: iter() __getitem__ ga, bool() __len__ ga qaytishi mumkin — bu mantiq bitta joyda, har turda takrorlanmaydi
  • Tezlik: funksiya tur slotiga to'g'ridan-to'g'ri murojaat qiladi (2.3)
  • Tarix: len va boshqa o'rnatilgan funksiyalar Python klasslari birlashtirilishidan (2.2 gacha o'rnatilgan turlar va foydalanuvchi klasslari alohida edi) oldin paydo bo'lgan

Dunder nomlar esa "bu interpretator chaqiradigan metod" degan konventsiya — ular oddiy metodlar bilan to'qnashmaydi.

3. Java: nominal interfeyslar

java
class Savat implements Iterable<Mahsulot> {
    public Iterator<Mahsulot> iterator() { ... }
}
  • Shartnoma aniq, kompilyator tekshiradi; IDE hamma amalga ega turlarni topadi
  • Metod nomida xato — kompilyatsiya xatosi
  • Tur yozilgan paytda interfeysni e'lon qilishi kerak — uchinchi tomon klassiga yangi interfeys "qo'shib" bo'lmaydi (adapter kerak)
  • Ko'p mayda interfeyslar (Comparable, Iterable, AutoCloseable...) va ularning kombinatsiyalari

4. Go: struktural interfeyslar

go
type Uzunlikli interface { Len() int }
// Len() int metodi bor HAR QANDAY tur avtomatik Uzunlikli
  • Python kabi e'lon qilish shart emas, lekin kompilyator tekshiradi
  • Interfeysni foydalanuvchi tomonida kichik qilib belgilash odat ("accept interfaces")
  • Tasodifiy moslik: nomi va imzosi mos kelgan, lekin ma'nosi boshqa metod interfeysga "tushib qoladi"
  • Operatorlarni yuklash yo'q — protokollar faqat oddiy metodlar orqali

5. Python ikki dunyoni birlashtirishga harakat qiladi

Ehtiyoj Vosita
Ish vaqtida "shu protokolmi?" collections.abc + __subclasshook__
Nominal shartnoma va mixin metodlar abc.ABC dan meros, register()
Statik struktural tekshiruv typing.Protocol + mypy/pyright
Ish vaqtida struktural tekshiruv @typing.runtime_checkable Protocol

6. Kutubxona yozuvchi uchun amaliy xulosa

  1. Foydalanuvchi turlarini protokol bo'yicha qabul qiling (Iterable[T], Sized) — aniq tur (list) emas
  2. O'z turlaringizni collections.abc dan meros qilib yozing — isinstance va mixinlar ishlaydi
  3. Ommaviy API uchun typing.Protocol bilan statik shartnoma yozing
  4. Ish vaqtida "qo'llab-quvvatlaydimi?" — amalni sinab ko'ring (EAFP)

7. Xulosa

  1. Python protokollari yashirin va struktural, Java — aniq va nominal, Go — struktural va statik tekshiriladigan
  2. len(x) funksiya bo'lgani uchun interpretator turda qidirish, natijani tekshirish, zaxira yo'llar va tezlikni markazlashgan holda ta'minlaydi
  3. Dunder nomlar interpretator chaqiradigan metodlarni oddiy metodlardan ajratadi
  4. Yashirin shartnomaning kamchiligini collections.abc va typing.Protocol to'ldiradi
  5. Kutubxonalarda protokol bo'yicha qabul qilish va ABC dan meros bilan yozish — ikki dunyoning afzalliklarini birlashtiradi

Nimani mustahkamlaydi: 2.1–2.6-bo'limlar.


Xulosa

Bu darsda Python ma'lumot modelini yaxlit tizim sifatida ko'rib chiqdik.

Eng muhim uch fikr:

  1. Hamma narsa — identifikatsiya, tur va qiymatga ega obyekt, xatti-harakatni esa tur belgilaydi. Sonlar, funksiyalar, klasslar va modullar ham obyekt; object meros ildizi, type esa barcha klasslarning turi va o'zining ham turi. Shuning uchun maxsus metodlar turga yoziladi va __class__ ni almashtirish obyektning butun xatti-harakatini o'zgartiradi.

  2. Sintaksis maxsus metodni obyektdan emas, turdan qidiradi. len(x) — type(x).__len__(x): nusxaga qo'shilgan dunder, __getattribute__ va __getattr__ chetlab o'tiladi. Bu tezlik (tur slotlari) va metaklasslar (repr(int)) uchun zarur, lekin __getattr__ asosidagi proksilarni buzadi — maxsus metodlarni klassga yozish yoki generatsiya qilish kerak.

  3. Protokollarning zaxira zanjirlari va o'chirish yo'li bor. bool __len__ ga, in va iter __getitem__ ga, reversed __len__ + __getitem__ ga, != __eq__ ga, str __repr__ ga qaytadi; __eq__ yozilsa __hash__ None bo'ladi. Keraksiz protokolni = None bilan o'chiring. collections.abc tekshiruvlari zaxira yo'llarni tanimaydi — "qo'llab-quvvatlaydimi?" savoliga amalni sinab javob bering, yangi turlarni esa ABC dan meros qilib yozing.

Keyingi darsda obyekt hayot davrining boshlanishini o'rganamiz: __new__ va __init__ farqi, o'zgarmas turlardan meros, singleton va nusxalar keshi, __init__ qachon chaqirilmaydi va __new__ dan boshqa tur qaytarilganda nima bo'ladi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
10.15-dars: Python ma'lumot modeli — IlmHamroh