Mundarija (20)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Obyekt: identifikatsiya, tur, qiymat
- 2.2. Sintaksis → maxsus metod xaritasi
- 2.3. Maxsus metodlar turda qidiriladi
- 2.4. Zaxira zanjirlari
- 2.5. Protokolni o'chirish: = None
- 2.6. collections.abc va haqiqiy xatti-harakat
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Obyekt, tur va qiymat
- Misol 2 — Maxsus metodlar turda qidiriladi
- Misol 3 — Zaxira zanjirlari va protokolni o'chirish
- Misol 4 — Amaliy: protokollar xaritasi
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
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:
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 iterableroyxat.__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;
typevaobjectmunosabati - Sintaksis → maxsus metod xaritasi (protokollar guruhlari)
- Maxsus metodlar turda qidiriladi — va bu nimani buzadi
- Zaxira zanjirlari:
in,iter,bool,reversed,!=,str,format - Protokolni
Nonebilan o'chirish collections.abctekshiruvlari 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:
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, Trueobject— 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?
- 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 - Metaklass muammosi. Agar
repr(x)avvalx.__repr__ni qidirganda,repr(int)int.__repr__ni — ya'niintnusxalari uchun metodni — topib,selfsiz 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:
class Hisob:
__hash__ = None # xeshlanmaydi (o'zgaruvchan tur)
class Satrlar:
def __getitem__(self, i): ...
__iter__ = None # iter() va for — TypeError; __getitem__ zaxirasi ham ishlamaydiBu 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,TypeErrorni ushlash Sequence,Mappingkabi murakkab ABC lar faqat meros yokiregister()bilan taniladi (ular__subclasshook__ishlatmaydi)- Yangi turni yozganda ABC dan meros oling — mixin metodlarni (
__contains__,__iter__,index,count...) bepul olasiz vaisinstanceto'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 yozingZaxiralar
bool: __bool__ → __len__ → True
in: __contains__ → __iter__ → __getitem__
iter: __iter__ → __getitem__
reversed: __reversed__ → __len__ + __getitem__
!=: __ne__ → not __eq__
str: __str__ → __repr__
hash: __eq__ yozilsa → NoneO'chirish va tekshirish
__hash__ = None __iter__ = None
try: iter(x) # isinstance(x, Iterable) dan ishonchliroq
except TypeError: ...4. Batafsil misollar
Misol 1 — Obyekt, tur va qiymat
"""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:
=== 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 subclassesNima ko'rsatdi: 2.1-bo'lim.
Misol 2 — Maxsus metodlar turda qidiriladi
"""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:
=== 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 argumentNima ko'rsatdi: 2.3-bo'lim.
Misol 3 — Zaxira zanjirlari va protokolni o'chirish
"""__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:
=== 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.
"""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:
=== 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__ = NoneNima 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
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
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
if isinstance(x, abc.Iterable): # ❌ __getitem__ turlarini o'tkazib yuboradi
...
try:
it = iter(x) # ✅
except TypeError:
...4. __eq__ yozib, xeshni unutish
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
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 qaytadi6. __bool__ o'rniga __len__ ning tasodifiy ta'siri
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
int.__repr__() # ❌ TypeError — bu int NUSXALARI uchun metod
type.__repr__(int) # ✅ klass uchun — repr(int)8. O'chirilishi kerak bo'lgan protokolni ochiq qoldirish
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 taqiqlash7. 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 —
PyObjectva 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
Yangi tur — mos protokol to'plami. Konteyner bo'lsa
__len__,__getitem__,__iter__,__contains__; qiymat bo'lsa__eq__,__hash__,__repr__.collections.abcdan meros oling — mixin metodlar bepul,isinstanceto'g'ri.Maxsus metodlarni klassga yozing, nusxaga yoki
__getattr__ga tayanmang.Har turga
__repr__—__str__ixtiyoriy.Zaxira yo'llarni biling — lekin yangi kodda asosiy protokolni aniq yozing (
__iter__,__bool__).Keraksiz meros qilingan protokolni
= Nonebilan o'chiring."Qo'llab-quvvatlaydimi?" savoliga amalni sinab javob bering —
iter(x),hash(x)— ABC tekshiruvi emas.Proksi va mock kutubxonalaridan foydalaning — maxsus metodlarni o'zingiz qo'lda uzatishdan oldin.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
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
[0, 10, 20]—__getitem__zaxirasi,IndexErrorda to'xtaydiTrue FalseFalse—Iterablefaqat__iter__ni tekshiradi3 1— aniq murojaatlar__getattr__orqali o'tadiTypeError—len()turda qidiradiTrue False—__ne____eq__ning inkoriNone—__eq__yozilgani uchunTrue 7— atribut bor, lekinlen(k)uni ishlatmaydiFalse yo'q—__len__0True True TrueS! S!—__str__yo'q →__repr__,format→strTypeError—__iter__ = Nonezaxirani ham o'chiradi
Vazifa 2: Xatolarni tuzating
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
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.malumotVazifa 3: Tekshiruvchi protokol auditori
Misol 4 ni kengaytiring:
- Barcha 2.2-jadvaldagi guruhlar uchun protokollarni aniqlasin (
with,async with,await,copy,pickle,formatspec,__index__) - Har protokolni haqiqiy amal bilan ham tekshirib, xarita bilan solishtirsin
- "Shubhali" holatlarni belgilasin:
__eq__bor,__hash__yo'q;__getitem__bor,__iter__yo'q;__str__bor,__repr__yo'q;__lt__bor,__le__yo'q vatotal_orderingishlatilmagan - Standart kutubxonadagi 20 ta turni tekshirib, qiziq topilmalarni yozing
Vazifa 4: To'liq proksi
ToliqProksi(asl) yozing:
- Barcha maxsus metodlarni (
list,dict,int,strning birlashmasi) turga uzatsin —aslturiga qarab kerakli klassni yaratib keshlasin - Har murojaat va chaqiruvni vaqt bilan jurnalga yozsin
isinstance(proksi, list)—Truebo'lishi uchun__class__xossasini yuklang va nima uchun bu ishlashini tushuntiringpickle,copy.deepcopyva==proksi orqali asl obyekt bilan qanday ishlashini tekshiring
Vazifa 5: O'z ketma-ketlik turingiz
HalqaBufer(sigim) — oxirgi sigim ta elementni saqlaydigan tur:
collections.abc.MutableSequencedan meros olib, faqat majburiy abstrakt metodlarni yozing- ABC dan bepul kelgan metodlarni (
append,extend,pop,remove,__iadd__,index,count,__contains__,__reversed__) ro'yxatlab, har birini sinang - Salbiy indekslar va kesmalar (
slice) ni qo'llab-quvvatlang __eq__(boshqa ketma-ketliklar bilan),__repr__va__hash__ = None- Tezlik: ABC dan kelgan
__contains__va o'zingiz yozgan versiyanitimeitbilan solishtiring
Vazifa 6: Rostlik auditi
Loyihangizdagi (yoki kursdagi) barcha klasslar uchun:
if obyekt:qayerda ishlatilganiniastbilan toping- Shu klasslarda
__bool__yoki__len__bor-yo'qligini aniqlang __len__bor, lekin__bool__yo'q klasslar uchun "bo'sh konteyner yolg'on" mantiqi kutilganmi — tekshiruv ro'yxatini yozingif x:niif 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
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, "
lenva 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" — nomilength,size,countbo'lishi mumkin edi. Umumiy amallar uchun yagona nom - Nazorat:
len()natija manfiy bo'lmagan butun son ekanini tekshiradi (__len__-1qaytarsa —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:
lenva 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
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
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
- Foydalanuvchi turlarini protokol bo'yicha qabul qiling (
Iterable[T],Sized) — aniq tur (list) emas - O'z turlaringizni
collections.abcdan meros qilib yozing —isinstanceva mixinlar ishlaydi - Ommaviy API uchun
typing.Protocolbilan statik shartnoma yozing - Ish vaqtida "qo'llab-quvvatlaydimi?" — amalni sinab ko'ring (EAFP)
7. Xulosa
- Python protokollari yashirin va struktural, Java — aniq va nominal, Go — struktural va statik tekshiriladigan
len(x)funksiya bo'lgani uchun interpretator turda qidirish, natijani tekshirish, zaxira yo'llar va tezlikni markazlashgan holda ta'minlaydi- Dunder nomlar interpretator chaqiradigan metodlarni oddiy metodlardan ajratadi
- Yashirin shartnomaning kamchiligini
collections.abcvatyping.Protocolto'ldiradi - 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:
Hamma narsa — identifikatsiya, tur va qiymatga ega obyekt, xatti-harakatni esa tur belgilaydi. Sonlar, funksiyalar, klasslar va modullar ham obyekt;
objectmeros ildizi,typeesa barcha klasslarning turi va o'zining ham turi. Shuning uchun maxsus metodlar turga yoziladi va__class__ni almashtirish obyektning butun xatti-harakatini o'zgartiradi.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.Protokollarning zaxira zanjirlari va o'chirish yo'li bor.
bool__len__ga,invaiter__getitem__ga,reversed__len__+__getitem__ga,!=__eq__ga,str__repr__ga qaytadi;__eq__yozilsa__hash__Nonebo'ladi. Keraksiz protokolni= Nonebilan o'chiring.collections.abctekshiruvlari 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.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!