İnheritance sözü Azərbaycan dilinə irsilik, törəmə kimi tərcümə olunur.
İnheritance bizə parent class-dan method və attribute-ları irsi olaraq götürməyə imkan yaradır. Bu yolla biz parent classa heç bir zərər vermədən yeni subclass-lar yaradıb subclass-lara öz istədiyimiz funksionallıqları əlavə edə bilərik.
Aşağıdakı kod parçasına fikir verək, Employee adında bir class-ımız var. inheritance-ı məşq etmək üçün developer-lər və manager-lər sublcass-ı yaratmaq məntiqli olardı. Çünki həm developer-lər həm də manager-lər işçilərdir və onların sistemdə ad,soyad, email və maaşının qeydə alınmağı məntiqli səslənir.
classEmployee: raise_amt =1.04def__init__(self,first,last,pay): self.first = first self.last = last self.email = first +'.'+ last +'@email.com' self.pay = paydeffullname(self):return'{}{}'.format(self.first, self.last)defapply_raise(self): self.pay =int(self.pay + self.raise_amt)emp_1 =Employee('Mammad', 'Mammadov', 50000)emp_2 =Employee('Mammad2', 'Mammadov2', 60000)print(emp_1.email)print(emp_2.email)
Yuxarıdakı kodun 16-cı sətrindən etibarən dəyişiklik etməyə başladım.
Pythonda subclass yaratmaq çox sadədir. Birinci normal qaydada class keyword-ündən istifadə edib ondan sonra class-ın adını qoymaq lazımdır. Bundan sonra isə mötərizə içində yeni yaranan classın hansı class-dan method və attribute-ları inherit etməsini istədiyimizi demək lazımdır (bax: aşağıdakı kodda sətir 16 və 19). Fikir verirsinizsə, sətir 17 və 20-də pass keyword-ünü işlətmişəm.
Python-da loop-ların, funksiyaların, şərt operatorlarının içində kod olmamağı error yaradır.
pass keyword-ü vasitəsiylə error yaratmadan kodun işləməsini təmin etmək mümkündür.
pass keyword-ü həmçinin, gələcəkdə yazılacaq kodu bildirmək üçün də istifadə olunur.
Ancaq burada mənim pass işlətməyimin səbəbi başqadır. Mən faktiki olaraq Developer və Manager subclass-ları içində nəsə yazmasam belə, 22 və 23-cü sətirlərdə onlardan istifadə etmişəm. Bəs hər iki kod necə eyni nəticəni verib?
Ona görə ki, Developer və Manager sub class-ları hər ikisi Employee class-ının içindəki kodları özlərinə irsi olaraq götürüblər. Python kodu çalışdırıldığı zaman kod yuxarıdan aşağı oxunub gəlib çatır 22-ci sətirə. Görür ki Developer və Manager class-larından istifadə olunub. Baxır ki nə Developer class-ının içində nə də Manager class-ının içində init methodu yoxdur. Ona görə qalxır Developer və Manager class-larının parent classına yəni Employee classına. Buna termin olaraq
"method resolution order" deyilir.
classEmployee: raise_amt =1.04def__init__(self,first,last,pay): self.first = first self.last = last self.email = first +'.'+ last +'@email.com' self.pay = paydeffullname(self):return'{}{}'.format(self.first, self.last)defapply_raise(self): self.pay =int(self.pay * self.raise_amt)classDeveloper(Employee):passclassManager(Employee):passdev_1 =Employee('Mammad', 'Mammadov', 50000)man_1 =Employee('Mammad2', 'Mammadov2', 60000)print(dev_1.email)print(man_1.email)
Method resolution order-i görməyin bir yolu da python-un help method-undan istifadə etməkdir. Əgər yuxarıdaki kodun sonunda bunu yazsaq:
print(help(Developer))
Bu nəticəni alarıq:
Help on class Developer in module __main__:classDeveloper(Employee) | Developer(first,last,pay) | | Methodresolutionorder:| Developer| Employee| builtins.object|| Methods inherited from Employee:||__init__(self, first, last, pay)| Initialize self. See help(type(self))for accurate signature.||apply_raise(self)||fullname(self)|| ----------------------------------------------------------------------| Data descriptors inherited from Employee:||__dict__| dictionary for instance variables (if defined)||__weakref__|list of weak references to the object(if defined)|| ----------------------------------------------------------------------| Data and other attributes inherited from Employee:|| raise_amt = 1.04
Yuxarıdaki output-un 6-cı sətrindən görə bilərsiniz ki, Developer subclass-ının Method resolution orderi necədir. İlkin olaraq lazım olan şey Developer subclass-ında axtarılır, bundan sonra Employee-də axtarılır. Əgər onda da tapılmazsa, builtins.object-də axtarılır.
Siz həmçinin 11-ci sətirdən etibarən Employee class-ından inherit olunan method-ları, 30-cu sətirdən etibarən isə inherit olunan data və digər attribute-ları görə bilərsiniz.
İndi isə gəlin baxaq ki, hər bir subclass-a necə öz method və attribute-larını əlavə edə bilərik. Əsas kodun 2-ci sətrindən görə biləcəyiniz kimi, Employee classında raise_amt = 1.04 (yəni 4 faiz) təyin olunub.
raise_amt =1.04
Maaş artımı üçün yazdığımız funksiya isə bucürdür:
Ancaq mən istəyirəm ki, Manager subclass-ının maaş artımı başqa, Developer subclass-ınınki isə başqa olsun. Bu halda nə etməliyəm? Bu halda hər bir class-da həmin adda istədiyimiz dəyərdə attribute yaratmaq lazımdır.
# Yuxarıdakı kodlar çox yer tutmasın deyə kəsilib.classDeveloper(Employee): raise_amt =1.06# Developer subclass-ının özünə məxsus maaş artımı faizi.classManager(Employee): raise_amt =1.10# Manager subclass-ının özünə məxsus maaş artımı faizi.dev_1 =Developer('Mammad', 'Mammadov', 50000)man_1 =Employee('Mammad2', 'Mammadov2', 60000)print(dev_1.pay)# Maaş artırmadan maaşları ekrana çap edirəm.print(man_1.pay)# dev_1.apply_raise()# Həm dev_1 həm man_1 maaşlarını qaldırıram. apply_raise() methoduman_1.apply_raise()# vasitəsiylə.print(dev_1.pay)# Həm dev_1 həm man_1 Maaş artımından sonrakı maaşlarını ekrana çapprint(man_1.pay)# edirəm.
Output:
50000
60000
53000
66000
Subclass-a parent class-da olmayan methodların verilməsi
Fərz edək ki, bizə lazımdır ki, hər bir developer-i yaradarkən onun adı, soyadı, maaşından əlavə həmçinin onun əsas istifadə etdiyi proqramlaşdırma dilini də təyin edək.
dev_1 =Developer('Mammad', 'Mammadov', 50000)
Ancaq bu hal-hazırki kodda mümkün deyil çünki bundan qabaq print(help(Developer)) yazaraq Developer subclass-ının 3 arqument götürdüyünü görmüşdük.
Help on class Developer in module __main__:classDeveloper(Employee) | Developer(first,last,pay) # Çox yertutmaması üçün redaktəolunub.
Həmin 3 arqument isə Employee parent class-ından törəyən arqumentlər idi. Hansı ki, kodun bu hissəsində qeyd olunub.
def__init__(self,first,last,pay): self.first = first self.last = last self.email = first +'.'+ last +'@email.com' self.pay = pay
Əgər biz Developer subclass-ı üçün ayrıca developerin istifadə etdiyi proqramlaşdırma dilini də daxil etmək istəyiriksə, bunun üçün kodda bucür dəyişiklik etmək lazım gələcək. Biz yuxarıda göstərdiyim kodun hamısını koplayalıb Developer subclass-ının içinə də ata bilərik. Ancaq bu kodun təkrar istifadəsinə gətirib çıxaracaq. OOP isə əslində tamamilə təkrar kod istifadəsinin qarşısını almaq üçün istifadə olunur. Beləliklə yeni tam versiya kodumuz bu cür görsənəcək.
classEmployee: raise_amt =1.04def__init__(self,first,last,pay): self.first = first self.last = last self.email = first +'.'+ last +'@email.com' self.pay = paydeffullname(self):return'{}{}'.format(self.first, self.last)defapply_raise(self): self.pay =int(self.pay * self.raise_amt)classDeveloper(Employee): raise_amt =1.06def__init__(self,first,last,pay,prog_lang):super().__init__(first, last, pay) self.prog_lang = prog_langclassManager(Employee): raise_amt =1.10dev_1 =Developer('Mammad', 'Mammadov', 50000, 'Python')man_1 =Manager('Mammad2', 'Mammadov2', 60000)print(dev_1.prog_lang)
Bizim taskımız bundan ibarət idi ki, proqramistləri təyin edəndə həmçinin həmin proqramistin istifadə etdiyi proqramlaşdırma dilini də təyin edə bilək. Bunu ümumi Employee classı üçün kodlamağımız mənasız olardı, çünki bu Employee class-ının Developer subclass-ından başqa bütün digər subclass-larında lazımsız prog_lang atributu yaradacaqdı. Bunun qarşısını almaq üçün bizə lazımdır ki, Developer subclass-ının içində init yaradaq.
Xatırlayırsınzısa method resolution order haqqında danışanda qeyd etmişdim ki compiler subclass-ın init methodu olmadıqda onun parent class-ına keçir.
Sətir 19-da görə bilərsiniz ki, Developer sublcass-ının içində yeni init methodu yaratmışıq və o prog_lang atributunu da istəyir.
super() built-in keyword
Bəs 20-ci sətirdə yazdığımız super() nə deməkdir?
super() bizə imkan verir ki, parent class-da olan init methodunun hamısını bir də kopyalamayaq. Onu kopyalamaq əvəzinə super() yazmaqla biz parent class-ındaki init methodunda nə təyin olunubsa hamısını işlədə bilirik. Parent class-dakı init methodunu çəkməyin bir neçə yolu var. Onların yazılış fərqlərini aşağıdakı tab-lara baxaraq görə bilərsiniz.
Nəzərinizə çatdırmaq istəyirəm ki, aşağıdakı kodların üçü də tamamilə eyni işi görür.
classDeveloper(Employee): raise_amt =1.06def__init__(self,first,last,pay,prog_lang): self.prog_lang = prog_langsuper().__init__(first, last, pay)
classDeveloper(Employee): raise_amt =1.06def__init__(self,first,last,pay,prog_lang): self.prog_lang = prog_lang self.first = first self.last = last self.email = first +'.'+ last +'@email.com' self.pay = pay
classDeveloper(Employee): raise_amt =1.06def__init__(self,first,last,pay,prog_lang): self.prog_lang = prog_lang Employee.__init__(self, first, last, pay)
İndi isə gəlin Manager subclass-ına elə dəyişikliklər edək ki, managerlərin digər işçilərdən kimi idarə etdiyi təyin edilə bilsin. Developer subclass-ında yazdığımız kimi Manager subclass-ında da super() işlətməliyik çünki Manager subclassına aid olan insanların da adı, soyadı və maaşı olacaq. Bundan başqa, Manager-in manage etdiyi insanları özündə saxlaması üçün, add_emp metodunu, özündəki siyahıdan çıxartması üçün remove_emp methodunu, özündəki siyahını çap etməsi üçün isə print_emp methodunu yazaq.
# Kodun yuxarı hissəsi kəsilib.classManager(Employee): raise_amt =1.10# Aşağıda employees=None olmasının səbəbi budur ki, PEP8 standartına görə# arqument olaraq Mutable object daxil etmək məqsədəuyğun deyil.# Bunun yerinə employees variable-nı None-a bərabər edib if vasitəsi ilə# onu dictionary-ə çevirmək lazımdır.def__init__(self,first,last,pay,employees=None):super().__init__(first, last, pay)# Employee classından atributlar inherit olur.if employees isNone: self.employees = []else: self.employees = employees# Əgər daxil edilən dəyər employees-də olmazsa, həmin dəyəri employees-ə əlavə et.defadd_emp(self,emp):if emp notin self.employees: self.employees.append(emp)# Əgər daxil edilən dəyər employees-də mövcuddursa, onu employees-dən sil.defremove_emp(self,emp):if emp in self.employees: self.employees.remove(emp)# Bu method manager-in manage etdiyi şəxslərin siyahısını çıxardır.defprint_emp(self):# İşçisi ekrana çıxacaq olan managerin fullname-i çap olunur birinci növbədə.print('{} is managing these people'.format(self.fullname()))for emp in self.employees:# employees siyahısındakı hər bir employeenin print('->', emp.fullname())# fullname-ini ekrana çap edir.dev_1 =Developer('Mammad', 'Mammadov', 50000, 'Python')emp_1 =Employee('Mammad2', 'Mammadov2', 50000)man_1 =Manager('Mammad3', 'Mammadov3', 60000)man_1.add_emp(dev_1)# man_1-in manage etdiyi insan siyahısına dev_1-i əlavə edir.man_1.add_emp(emp_1)# man_1-in manage etdiyi insan siyahısına emp_1-i əlavə edir.man_1.print_emp()# man_1-in manage etdiyi insan siyahısını ekrana çap edir.man_1.remove_emp(dev_1)# dev_1-i man_1-in manage etdiyi insanlar siyahısından silir.man_1.print_emp()# man_1-in manage etdiyi insan siyahısını ekrana çap edir.
Output:
Mammad3 Mammadov3 is managing these people
-> Mammad2 Mammadov2
-> Mammad Mammadov
Mammad3 Mammadov3 is managing these people
-> Mammad Mammadov
Yuxarıdakı misalda kod parçalarının nə etdiyini comment şəklində izah etmişəm.
isinstance() və issubclass()
Bu methodların ikisi də cavab olaraq True vəya False qaytarır.