#32 DUNDER METODLAR

Pythondagi maxsus metodlar bilan tanishamiz.

MAXSUS METODLAR

Pythonda obyektlar bilan ishlashni yanada qulay qilish uchun bir nechta maxsus metodlar bor. Bu metodlarning nomi ikki pastki chiziq bilan yozilgani uchun, double underscore yoki qisqa qilib dunder metodlar deb ataladi. Dunder metolar yordamida obyektlarga qo'shimcha qulayliklar va vazifalar qo'shishimiz mumkin. Klass yoki obyektga oid dunder metodlar ro'yxatini ko'rish uchun dir() funksiyasidan foydalanamiz:
>>> dir(Avto)
['_Avto__num_avto',
'__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'make',
'model',
'narh',
'rang',
'yil']
Dunder metodlardan biz __init__ metodi bilan tanishdik. Bu metod klassdan obyekt yaratishda chaqiriladi va obyektning xususiyatlarini belgilaydi. Ushbu darsimizda esa maxsus metodlarning ba'zilari bilan tanishamiz.

OBYEKT HAQIDA MA'LUMOT

Obyektga print() yoki str() orqali murojat qilinganda obyekt haqida tushunarli ma'lumot qaytarish uchun __repr__va __str__ metodlaridan foydalanamiz. Tushunarli bo'lishi uchun avvalgi darsimizdagi Avto klassiga qaytamiz:
class Avto:
__num_avto = 0
"""Avtomobil klassi"""
def __init__(self,make,model,rang,yil,narh):
"""Avtomobilning xususiyatlari"""
self.make = make
self.model = model
self.rang = rang
self.yil = yil
self.narh = narh
Avto.__num_avto += 1
Yuqoridagi klassdan yangi obyekt yaratamiz va obyekt haqida ma'lumot olish uchun print() funksiyasini chaqiramiz:
avto1 = Avto("GM","Malibu","Qora",2020,40000)
print(avto1) # obyekt haqida ma'lumot
Natija: <__main__.Avto object at 0x00000238A6DAE0C8>
Qandaydur tushunarsiz ma'lumot. Ekrandagi natijadan biz faqat avto1 obyektimiz Avto klassiga tegishli ekanini ko'ramiz. Qanday qilib shuning o'rniga obyekt haqida tushunarliroq ma'lumot olishimiz mumkin?
Gap shundaki biz har gal obyketga print() (yoki str() yoki repr()) orqali murojat qilganimizda, Python obyket ichida __str__ yoki __repr__ metodlariga murojat qiladi. Agar biz bu metodlarni yozmagan bo'lsak, yuqoridagi kabi umumiy ma'lumot qayataradi.
Biz ushbu metodlarni yangidan yozib, biz istagan ma'lumotni qayataradian qilishimiz mumkin. Odatda, yuqoridagi ikki metoddan birini yozish kifoya. Odatda, __repr__ umumiyorq, __str__ esa batafsilroq ma'lumot olish uchun ishlatiladi.
Ikkalasidan birini tanlaganda, __repr__metodiga yon bosiladi, sababi bu metod print(), str() va repr() funksiyalarining hammasi bilan ishlaydi. Keling biz ham yuoqirdagi klassimizga__repr__metodini qo'shamiz:
class Avto:
__num_avto = 0
"""Avtomobil klassi"""
def __init__(self,make,model,rang,yil,narh):
"""Avtomobilning xususiyatlari"""
self.make = make
self.model = model
self.rang = rang
self.yil = yil
self.narh = narh
Avto.__num_avto += 1
def __repr__(self):
"""Obyekt haqida ma'lumot"""
return f"Avto: {self.rang} {self.make} {self.model}"
Qaytadan print() funksiyasini chaqiramiz:
avto1 = Avto("GM","Malibu","Qora",2020,40000)
print(avto1)
Natija: Avto: Qora GM Malibu
Mana endi natijamiz ancha tushunarli ko'rinishda chiqdi.

OBYEKTLARNI TAQQOSLASH

Taqqoslash operatorlari yordamida biz turli obyektlarni solishtirishimiz mumkin. Taqqoslash natijasi mantiqiy qiymat (True yoki False) ko'rinishida bo'ladi.
x,y = 5,10
print(x>y)
Natija: False
Keling endi Avto klassidan 2 ta obyekt yaratamiz, va ularni taqqoslab ko'ramiz:
avto1 = Avto("GM","Malibu","Qora",2020,40000)
avto2 = Avto("GM","Lacetti","Oq",2020,20000)
avto1>avto2
Natija: TypeError: '>' not supported between instances of 'Avto' and 'Avto'
Xatolik. Demak bu ikki obyektni solshtirib bo'lmas ekan.
Biz taqqolash operatorlariga murojat qilganimizda, Python obyektlar ichida taqqoslash uchun maxsus metodlarni qidiradi, agar metodlar topilmasa yuqoridagi kabi TypeError qaytaradi.
Taqqoslash metodlari quyidagilardan iborat:
Metod
Operator
x.__lt__(self,y)
x<y
x.__le__(self,y)
x<=y
x.__gt__(self,y)
x>y
x.__ge__(self,y)
x>=y
x.__eq__(self,y)
x==y
x.__ne__(self,y)
x!=y
Yuqoridagi obyektlardan yarmi uchun metodlar yozishimiz kifoya, misol uchun __lt__ (x<y) metodini yozsak, __gt__ (x>y) metodini yozishimiz shart emas, yoki __le__ metodi, __ge__metodini ham o'z ichiga oladi, va hokazo.
Keling tushunarli bo'lishi uchun Avto klassiga obyektlarni solishtirish uchun metod yozamiz. Deylik, biz obyektlarni narhi bo'yicha solishtirmoqchimiz, unda klassimizga quyidagi metodlarni qo'shamiz (klassni to'liq yozmadik, faqat qo'shilgan metodlarni keltiramiz):
def __eq__(self,boshqa_avto):
"""Tenglik"""
return self.narh == boshqa_avto.narh
def __lt__(self,boshqa_avto):
"""Kichik"""
return self.narh<boshqa_avto.narh
def __le__(self,boshqa_avto):
"""Kichik yoki teng"""
return self.narh<=boshqa_avto.narh
Kodimizga e'tibor qilsangiz biz tenglik (__eq__) yoki kichiklikni (__lt__) tekshirmoqchi bo'lganimizda ikki avtoning aynan narhi bo'yicha solishtiramiz (self.narh == boshqa_avto.narh).
Mana endi avtolarni solishtirsak bo'ladi:
avto1 = Avto("GM","Malibu","Qora",2020,40000)
avto2 = Avto("GM","Lacetti","Oq",2020,20000)
print(avto1>avto2)
Natija: False
avto3 = Avto("Honda","Accord","Oq",2017,40000)
print(avto1==avto3)
Natija: True

OBYEKT UZUNLIGI

Pythonda len() funksiyasi yordamida turli obyektlarning uzunligini bilishimiz mumkin, misol uchun matn, ro'yxat, lug'at, set va hokazo.
matn = 'hello world'
print(len(matn))
Natija: 11
sonlar = [12, 34, 56, 66]
print(len(sonlar))
Natija: 4
Biz len() funksiyasiga murojat qilganimizda, Python funksiyaga uzatilgan obyektning ichidagi maxsus __len__ metodiga murojat qiladi. Agar bu metod mavjud bo'lmasa dasturimiz xato qaytaradi.
len(avto1)
Natija: TypeError: object of type 'Avto' has no len()
Misol uchun, bizning Avto klassimizda bu metod yozilmagan, shuning uchun Python TypeError xatosini qaytardi.
Kelin endi __len__metodini qanday ishlatishga ham misol ko'raylik.
Boshlanishiga, yangi, AvtoSalon degan klass yaratamiz. Bu klassimiz 2 ta xususiyatga ega: salon nomi (name) va salondagi mashinalar (avtolar).
class AvtoSalon:
"""Avtosalon klassi"""
def __init__(self,name):
self.name = name
self.avtolar = []
def __repr__(self):
return f"{self.name} avtosaloni"
Yuqoridagi klassdan yangi obyekt yaratamiz:
salon1 = AvtoSalon("MaxAvto")
print(salon1)
Natija: MaxAvto avtosaloni
AvtoSalon klassimizga __repr__metodini qo'shganimiz uchun natijamiz chiroyli ko'rinishda chiqdi.
Keling endi salonga avtomobil qo'shish uchun yangi add_avto() metodini yozamiz. Bu metodimiz Avto klassiga oid obyektlarni qabul qilishi kerak. add_avto() ga uaztilgan parametr Avto klassiga tegishli yoki yo'qligini tekshirish uchun maxsus isinstance() funksiyasidan foydalanamiz.
Bu funksiya biror obyekt ma'lum klassga tegishli ekanligini tekshirish uchun ishlatiladi:
>>> isinstance("salom",str)
True # "salom" bu str
>>> isinstance(9.5,int)
False # 9.5 int (butun son) emas
>>> isinstance(avto1,Avto)
True # avto1 Avto klassiga tegishli
add_avto() metodiga qaytamiz:
class AvtoSalon:
"""Avtosalon klassi"""
def __init__(self,name):
self.name = name
self.avtolar = []
def __repr__(self):
return f"{self.name} avtosaloni"
def add_avto(self,avto):
if isinstance(avto,Avto): # agar avto Avto klassidan bo'lsa
self.avtolar.append(avto)
else:
print("Avto obyketini kiriting")
Metodimizni tekshirib ko'ramiz:
# Avto obyektlarini yaratamiz
avto1 = Avto("GM","Malibu","Qora",2020,40000)
avto2 = Avto("GM","Lacetti","Oq",2020,20000)
avto3 = Avto("Toyota",'Carolla',"Silver",2018, 45000)
# Yuqoridagi obyektlarni salon1 ga qo'shamiz
for avto in [avto1, avto2, avto3]:
salon1.add_avto(avto)
Mana navbat __len__ metodiga. Biz bu metod yordamida len() funksiyasi salonimizdagi avtomobillar sonini qaytaradigan qilamiz. Buning uchun yuqoridagi AvtoSalon klassiga __len__ funksiyani ham qo'shamiz va uni obyekt ichidagi avtolar ro'yxatyining uzunligini qaytaradigan qilamiz:
def __len__(self):
return len(self.avtolar)
Mana endi bizning AvtoSalon klassimizga oid obyektlar uchun ham len() funksiyasini qo'llasak bo'ladi:
>>> print(len(salon1))
3 # Salonimizda 3 ta moshina bor

OBYEKT ELEMENTLARIGA MUROJAT QILISH

Ba'zi obyektlarning (matn, ro'yxat, lug'at va hokazo) elementlariga alohida murojat qilish mumkin.
>>> mevalar = ['olma','anor','uzum']
>>> mevalar[0]
'olma'
Bizning salonimizda ham 3 ta avto bor, ularni ko'rish uchun yuqoridagi kabi element raqami orqali murojat qila olamizmi?
salon1[0]
Natija: TypeError: 'AvtoSalon' object is not subscriptable
Afsuski yo'q. Ko'rib turganingizdek bizning obyektimizga bunday murojat qilib bo'lmas ekan. Obyektimizga bu xususiyatni qo'shish uchun __getitem__metodini yozishimiz kerak.
class AvtoSalon:
"""Avtosalon klassi"""
def __init__(self,name):
self.name = name
self.avtolar = []
def __repr__(self):
return f"{self.name} avtosaloni"
def __len__(self):
return len(self.avtolar)
def __getitem__(self,index):
return self.avtolar[index]
Endi salon1 obyektimizning elementlariga murojat qilinganda __getitem__metodi obyekt ichidagi avtolar ro'yxatidan ko'rsatilgan element (avtomobilni) qaytaradi.
>>> salon1[0]
Avto: Qora GM Malibu
>>> salon1[1]
Avto: Oq GM Lacetti
>>> salon1[2]
Avto: Silver Toyota Carolla
>>> salon1[:] # barcha elementlarni ko'rish
[Avto: Qora GM Malibu, Avto: Oq GM Lacetti, Avto: Silver Toyota Carolla]
Keling obyekt elementlaridan birini o'zgartirib ko'ramiz:
avto4 = Avto("Mazda", "6", 'Qizil',2015,35000)
salon1[0]=avto4
Natija: TypeError: 'AvtoSalon' object does not support item assignment
Yana xatolik. Gap shundaki __getitem__ metodi o'z nomi bilan (get) element qaytaruvchi metod. Biror elementni o'zgartirish uchun esa __setitem__metodini ham qo'shishimiz kerak. Bu metodimizga murojat qilinganda ham, yangi qiymat Avto klassiga oid ekanligini tekshirib olish maqsadga muvofiq bo'ladi:
def __setitem__(self,index,value):
if isinstance(value,Avto):
self.avtolar[index]=value
Qaytadan elementni o'zgartirishga harakat qilib ko'ramiz:
avto4 = Avto("Mazda", "6", 'Qizil',2015,35000)
salon1[0]=avto4
print(salon1[0])
Natija: Avto: Qizil Mazda 6
Kutilgan natija chiqdi

OPERATORLARNI QAYTA TALQIN QILISH (OVERLOADING)

Pythonda obyektlar o'rtasida turli arifmetik amallarni bajarish mumkin va bu amallar obyektning turiga qarab, Pytnon tomonidan turlicha talqin qilinadi. Masalan:
Sonlar:
>>> x,y=10,20
>>> x+y
30
>>> x*5
50
Matnlar:
>>> t1 = "hello"
>>> t2 = "world"
>>> t1+t2
'helloworld'
>>> t1*5
'hellohellohellohellohello'
Ro'yxatlar:
>>> l1 = [1, 2, 3]
>>> l2 = [4, 5, 6]
>>> l1+l2
[1, 2, 3, 4, 5, 6]
>>> l1*2
[1, 2, 3, 1, 2, 3]
va hokazo.
Keling, bu amallarni bizning obyektimizga ham qo'llab ko'ramiz. Boshlanishiga 2 ta avtosalon yarataylik, va har biriga alohida avtolar qo'shaylik. Ishimizni osonlashtirish uchun add_avto() metodimizni ham istalgancha parametr qabul qilishga moslab o'zgartiramiz:
class AvtoSalon:
"""Avtosalon klassi"""
def __init__(self,name):
self.name = name
self.avtolar = []
def __repr__(self):
return f"{self.name} avtosaloni"
def __len__(self):
return len(self.avtolar)
def __getitem__(self,index):
return self.avtolar[index]
def __setitem__(self,index,value):
if isinstance(value,Avto):
self.avtolar[index]=value
def add_avto(self,*qiymat):
for avto in qiymat:
if isinstance(avto,Avto):
self.avtolar.append(avto)
else:
print("Avto obyketini kiriting")
Obyektlarni yaratamiz:
salon1 = AvtoSalon("MaxAvto")
salon2 = AvtoSalon("Avto Lider")
avto1 = Avto("GM","Malibu","Qora",2020,40000)
avto2 = Avto("GM","Lacetti","Oq",2020,20000)
avto3 = Avto("Toyota",'Carolla',"Silver",2018, 45000)
avto4 = Avto("Mazda", "6", 'Qizil',2015,35000)
avto5 = Avto("Volkswagen","Polo",'Qora',2015,30000)
avto6 = Avto("Honda","Accord","Oq",2017,42000)
salon1.add_avto(avto1, avto2, avto3)
salon2.add_avto(avto4, avto5, avto6)
Mavzuga qaytamiz. Operatorlarni qayta talqin qilish. Keling boshlanishiga ikki obyektni qo'shib ko'ramiz:
salon1+salon2
Natija: TypeError: unsupported operand type(s) for +: 'AvtoSalon' and 'AvtoSalon'
Demak, bu ikki obyektni qo'shib bo'lmas ekan. Biz obyekt egasi sifatida qo'shish operatorini o'zimiz istagancha talqin qilishimiz mumkin. Misol uchun AvtoSalon obyektiga boshqa AvtoSalon obyektini qo'shganda, yangi AvtoSalon obyektini qaytaraylik va bu yangi obyekt avvalgi ikki obyektning avtolariga ega bo'lsin.
Qo'shish operatorini qayta talqin qilish uchun AvtoSalon klassimizga __add__ metodini qo'shamiz.
def __add__(self,qiymat):
if isinstance(qiymat,AvtoSalon):
yangi_salon = AvtoSalon(f"{self.name} {qiymat.name}")
yangi_salon.avtolar = self.avtolar + qiymat.avtolar
return yangi_salon
Qo'shish operatorini tekshirib ko'ramiz:
>>> salon3 = salon1+salon2
>>> print(salon3)
MaxAvto Avto Lider avtosaloni
>>> salon3[:]
[Avto: Qora GM Malibu,
Avto: Oq GM Lacetti,
Avto: Silver Toyota Carolla,
Avto: Qizil Mazda 6,
Avto: Qora Volkswagen Polo,
Avto: Oq Honda Accord]
Istasak, qo'shish operatori yordamida salonga yangi Avto qo'shish imkoniyatini ham yaratishimiz mumkin:
def __add__(self,qiymat):
if isinstance(qiymat,AvtoSalon):
yangi_salon = AvtoSalon(f"{self.name} {qiymat.name}")
yangi_salon.avtolar = self.avtolar + qiymat.avtolar
return yangi_salon
elif isinstance(qiymat,Avto):
self.add_avto(qiymat)
else:
print(f"AvtoSalon ga {type(qiymat)} qo`shib bo`lmaydi")
Tekshirib ko'ramiz:
avto7 = Avto("BMW", 'X7','Qora',2015,75000)
salon1 + avto7
print(salon1[:])
Natija: [Avto: Qora GM Malibu, Avto: Oq GM Lacetti, Avto: Silver Toyota Carolla, Avto: Qora BMW X7]
Huddi shu kabi boshqa operatorlarni ham o'zingiz istalgancha talqin qilishingiz mumkin:
Operator
Metod
Qo'shish
__add__
Ayirish
__sub__
Ko'paytirish
__mul__
Daraja
__pow__
Bo'lish
__div__

OBYEKTNI CHAQIRISH

Obyektlarni huddi funksiyalarni chaqirgandek chaqirish ham mumkin. Odatda biror funksiyaga murojat qilganda funksiya nomidan so'ng qavslar ochiladi va yopiladi. Agar funksiya argument qabul qilsa, ular qavs ichida beriladi.
>>> print(10)
10
>>> len("salom")
5
Biz obyektimizga ham shunday imkoniyat qo'shishimiz mumkin. Buning uchun maxsus __call__ metodidan foydalaniladi.

PARAMETRSIZ CHAQIRISH

Misol uchun, kelign yuqoridagi AvtoSalon klassiga oid obyektlar chaqirilganda salondagi avtomobillar ro'yxatini ko'rsatadigan qilaylik. Buning uchu AvtoSalon klassiga quyidagi metodni qo'shamiz:
def __call__(self):
return [avto for avto in self.avtolar]
Mana endi obyekt chaqirib ko'ramiz:
salon1()
Natija: [Avto: GM Malibu. 40000$, Avto: GM Lacetti. 20000$, Avto: Toyota Carolla. 45000$]

PARAMETR BILAN CHAQIRISH

Yuqorida salon1 ni parametrsiz chaqrishini ko'rdik. Keling endi parametr bilan chaqirishni ham yo'lga qo'yaylik. Bunda, yuborilgan parametr yangi avto obyekti bo'lsin, va bu parametr salondagi avtolar ro'yxatiga qo'shilsin. Metodimizni quyidagicha o'zgartiramiz:
def __call__(self,*param):
if param: # agar parametr bo'lsa
for avto in param:
self.add_avto(avto)
else: # agar parametr bo'lmasa
return [avto for avto in self.avtolar]
Endi bizning klassimizga ham parametr bilan, ham parametrsiz chaqirish imkoniyati qo'shildi.
avto_new = Avto("Mercedes-Benz", 'E200','Silver',2015,80000)
salon1(avto_new) # Yangi avto qo'shamiz
salon1() # salondagi mashinalarni ko'ramiz
Natija: [Avto: GM Malibu. 40000$, Avto: GM Lacetti. 20000$, Avto: Toyota Carolla. 45000$, Avto: Mercedes-Benz E200. 80000$]
__call__ metodini qanday talqin qilish ham sizning ihtiyoringizda.

SO'NGSO'Z

Ushbu bo'limda biz maxsus metodlarning ba'zilari bilan tanishdik. Bu metodlarning qulayligi shundaki, siz ularni istalgacha talqin qilishingiz va har bir obyekt uchun mos keladigan qilib yaratishingiz mumkin. Ko'rib turganingizdek dunder metodlar obyektingizning imkoniyatlarini kengaytiradi va ularga qo'shimcha vazifalar yuklaydi.

AMALIYOT

  • Avvalga darslarda yaratilgan obyektlarga (Shaxs, Talaba) turli dunder metodlarni qo'shishni mashq qiling.
    • Obyekt haqida ma'lumot (__rerp__)
    • Talabalarni bosqichi bo'yicha solishtirish (__lt__,__eg__ va hokazo)
  • Fan degan yangi klass yarating. Fan obyetklari tarkibida shu fanga yozilgan talabalarning ro'yxati saqlansin. Buning uchun Fanga add_student(), __getitem__, __setitem__, __len__ kabi metodlarni qo'shing.
  • Fanga qo'shish + operatori yordamida talaba qo'shish metodini yozing
  • Minus (-) operatori yordamida fandan talaba olib tashlash metodini yozing (bunda talabaning passport raqami yoki ID raqami bo'yicha topib, olib tashlash mumkin)
  • Fan obyektlarini chaqiriladigan qiling (masalan, fizika(), yoki fizika(talaba1)). Bu metodlarni o'zingiz istagandek talqin qiling.

KODLAR

GitHub

Repl.it