Moștenirea
Moștenirea permite crearea unei noi clase pe baza unei clase deja existente. Alături de încapsulare, moștenirea este unul dintre pilonii principali ai programării orientate pe obiecte.
Conceptele cheie ale moștenirii sunt subclasa și superclasa. Subclasa moștenește toate atributele și metodele publice ale superclasei. Superclasa mai este numită și clasă de bază (base class) sau clasă părinte (parent class), iar subclasa este denumită clasă derivată (derived class) sau clasă copil (child class).
Sintaxa pentru moștenirea claselor arată astfel:
class Subclasa(Superclasa):
metode_subclasa
De exemplu, avem o clasă Person, care reprezintă o persoană:
class Person:
def __init__(self, name):
self.__name = name # numele persoanei
@property
def name(self):
return self.__name
def display_info(self):
print(f"Nume: {self.__name}")
Să presupunem că avem nevoie de o clasă pentru un angajat, care lucrează la o anumită firmă. Am putea crea de la zero o nouă clasă, de exemplu, clasa Employee:
class Employee:
def __init__(self, name):
self.__name = name # numele angajatului
@property
def name(self):
return self.__name
def display_info(self):
print(f"Nume: {self.__name}")
def work(self):
print(f"{self.name} lucrează")
Cu toate acestea, clasa Employee poate avea aceleași atribute și metode ca și clasa Person, deoarece un angajat este o persoană. Astfel, în clasa Employee de mai sus doar adăugăm metoda work, restul codului repetând funcționalitatea clasei Person. Pentru a evita duplicarea funcționalității, în acest caz este mai bine să folosim moștenirea.
Așadar, vom moșteni clasa Employee din clasa Person:
class Person:
def __init__(self, name):
self.__name = name # numele persoanei
@property
def name(self):
return self.__name
def display_info(self):
print(f"Nume: {self.__name}")
class Employee(Person):
def work(self):
print(f"{self.name} lucrează")
tom = Employee("Tom")
print(tom.name) # Tom
tom.display_info() # Nume: Tom
tom.work() # Tom lucrează
Clasa Employee preia complet funcționalitatea clasei Person, adăugând doar metoda work(). Prin urmare, la crearea unui obiect Employee putem folosi constructorul moștenit de la Person:
tom = Employee("Tom")
Și putem accesa atributele/proprietățile și metodele moștenite:
print(tom.name) # Tom
tom.display_info() # Nume: Tom
Totuși, trebuie remarcat că pentru Employee nu sunt accesibile atributele private de tipul __name. De exemplu, nu putem în metoda work să accesăm atributul privat self.__name:
def work(self):
print(f"{self.__name} lucrează") # ! Eroare
Moștenire multiplă
O caracteristică distinctivă a limbajului Python este suportul pentru moștenirea multiplă, adică o clasă poate fi moștenită de la mai multe clase:
# clasa angajatului
class Employee:
def work(self):
print("Angajatul lucrează")
# clasa studentului
class Student:
def study(self):
print("Studentul studiază")
class WorkingStudent(Employee, Student): # Moștenire de la clasele Employee și Student
pass
# clasa studentului care lucrează
tom = WorkingStudent()
tom.work() # Angajatul lucrează
tom.study() # Studentul studiază
Aici este definită clasa Employee, care reprezintă un angajat, și clasa Student, care reprezintă un student. Clasa WorkingStudent, care reprezintă un student care lucrează, nu definește nicio funcționalitate, de aceea are operatorul pass. Clasa WorkingStudent doar moștenește funcționalitatea de la cele două clase Employee și Student. Prin urmare, putem apela metodele ambelor clase pentru un obiect al acestei clase.
În același timp, clasele moștenite pot fi mai complexe funcțional, de exemplu:
class Employee:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
def work(self):
print(f"{self.name} lucrează")
class Student:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
def study(self):
print(f"{self.name} studiază")
class WorkingStudent(Employee, Student):
pass
tom = WorkingStudent("Tom")
tom.work() # Tom lucrează
tom.study() # Tom studiază
Moștenirea multiplă poate părea convenabilă, totuși poate duce la confuzie dacă ambele clase moștenite conțin metode/atribute cu aceleași nume. De exemplu:
class Employee:
def do(self):
print("Angajatul lucrează")
class Student:
def do(self):
print("Studentul studiază")
class WorkingStudent(Employee, Student):
pass
tom = WorkingStudent()
tom.do() # ?
Ambele clase de bază - Employee și Student - definesc metoda do, care afișează șiruri diferite pe consolă. Ce implementare va folosi clasa moștenitoare WorkingStudent? La definirea clasei, primul în lista claselor de bază este clasa Employee:
class WorkingStudent(Employee, Student)
Prin urmare, implementarea metodei do va fi luată din clasa Employee.
Dacă am schimba ordinea claselor: atunci ar fi folosită implementarea din clasa Student.
class WorkingStudent(Student, Employee)
Dacă este necesar, putem programa ordinea aplicării funcționalității claselor de bază. Pentru aceasta se folosește atributul __mro__ sau metoda mro():
print(WorkingStudent.__mro__)
print(WorkingStudent.mro())