1. Đa kế thừa (multiple inheritance) trong Python
Một lớp con có thể kế thừa từ nhiều hơn một lớp cha. Đó gọi là đa kế thừa (multiple inheritance). Tất cả các thuộc tính và phương thức của tất cả lớp cho được kế thừa bởi lớp con.
class Base1:
pass
class Base2:
pass
class MultiDerived(Base1, Base2):
pass
class Person:
def info(self):
print("Info of a person.")
class Employee:
def showSalary(self):
print("Salary of an employee.")
class Teacher(Person, Employee):
pass
john = Teacher()
john.info()
john.showSalary()
Kết quả
Info of a person.
Salary of an employee.
Trong ví dụ trên, lớp Teacher kế thừa từ lớp Person và Employee. Lớp Teacher sẽ kế thừa và sử dụng các phương thức info()
và showSalary()
từ 2 lớp trên.
2. Method Resolution Order (MRO) trong Python
Có trường hợp các lớp cha có những phương thức cùng tên với nhau và lớp con gọi những phương thức này. Vậy thì phương thức của lớp cha nào sẽ được thực thi khi lớp con gọi đến?
Python sử dụng Method Resolution Order (MRO) để quy định phương thức được gọi trong trường hợp này. MRO chỉ ra một thứ tự lớp nào được ưu tiên tìm và gọi phương thức trước. Chúng ta có thể sử dụng thuộc tính __mro__
hoặc hàm mro()
để xem thứ tự này.
class Person:
def info(self):
print("Info of a person.")
def work(self):
print("A person works.")
class Employee:
def work(self):
print("An employee works.")
def showSalary(self):
print("Salary of an employee.")
class Teacher(Person, Employee):
pass
print(Teacher.__mro__)
john = Teacher()
john.work()
Kết quả
(<class '__main__.Teacher'>, <class '__main__.Person'>, <class '__main__.Employee'>, <class 'object'>)
A person works.
Trong ví dụ trên, lớp Person được ưu tiên tìm hàm work()
để thực thi trước do thứ tự kế thừa class Teacher(Person, Employee)
.
class Person:
def info(self):
print("Info of a person.")
def work(self):
print("A person works.")
class Employee:
def work(self):
print("An employee works.")
def showSalary(self):
print("Salary of an employee.")
class Teacher(Employee, Person):
pass
print(Teacher.mro())
john = Teacher()
john.work()
Kết quả
(<class '__main__.Teacher'>, <class '__main__.Employee'>, <class '__main__.Person'>, <class 'object'>)
An employee works.
Trong ví dụ trên, lớp Employee được ưu tiên tìm hàm work()
để thực thi trước do thứ tự kế thừa class Teacher(Employee, Person)
.
Lưu ý: Tất cả các lớp được tạo ra trong Python đều tự động kế thừa từ lớp object
.
3. Kế thừa nhiều cấp (multilevel inheritance) trong Python
Chúng ta cũng có thể tạo ra một lớp mới bằng cách kế thừa từ một lớp con. Đó gọi là kế thừa nhiều cấp (multilevel inheritance). Python không giới hạn số cấp kế thừa.
Trong multilevel inheritance, tất cả thuộc tính và phương thức của lớp cha và lớp con sẽ được kế thừa bởi lớp con mới.
class Base:
def methodBase(self):
print("This is a method of Base.")
class Derived1(Base):
def methodDerived1(self):
print("This is a method of Derived1.")
class Derived2(Derived1):
def methodDerived2(self):
print("This is a method of Derived2.")
# create an object of Derived2
obj = Derived2()
# call method inherits from Base
obj.methodBase()
# call method inherits from Derived1
obj.methodDerived1()
# call method of Derived2
obj.methodDerived2()
Kết quả
This is a method of Base.
This is a method of Derived1.
This is a method of Derived2.
Trong ví dụ trên, lớp Derived1 kế thừa lớp Base. Lớp Derived2 kế thừa lớp Derived1. Lớp Derived2 sẽ kế thừa tất cả thuộc tính và phương thức của lớp Base và Derived1.
4. Hàm __init__() trong đa kế thừa
Trong đa kế thừa trong Python, lớp con sẽ kế thừa hàm __init__()
của các lớp cha. Khi tạo một đối tượng của lớp con, hàm __init__()
nào sẽ được gọi? Như chúng ta đã biết, Python sẽ sử dụng Method Resolution Order (MRO) để giải quyết vấn đề này. Nhưng khi đó, có một vấn đề xảy ra. Chúng ta cùng xem ví dụ sau đây để thấy rõ vấn đề này.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(self.name + ",", self.age, "years old.")
class Employee:
def __init__(self, salary):
self.salary = salary
def showSalary(self):
print("salary:", self.salary)
class Teacher(Person, Employee):
pass
print(Teacher.mro())
john = Teacher("John", 35)
# call method inherits from Person
john.info()
# AttributeError: 'Teacher' object has no attribute 'salary'
john.showSalary()
Kết quả
[<class '__main__.Teacher'>, <class '__main__.Person'>, <class '__main__.Employee'>, <class 'object'>]
John, 35 years old.
Traceback (most recent call last):
File "c:\python-examples\main.py", line 20, in <module>
john.showSalary()
File "c:\python-examples\main.py", line 13, in showSalary
print("salary:", self.salary)
AttributeError: 'Teacher' object has no attribute 'salary'
Vấn đề ở đây là hàm __init__()
sử dụng để tạo ra một object của lớp Teacher là được kế thừa từ lớp Person. Lúc này, các thuộc tính của đối tượng có được chỉ là name và age được kế thừa từ lớp Person mà không có thuộc tính salary từ lớp Employee. Giải pháp trong những trường hợp này là định nghĩa hàm __init__()
cho lớp Teacher.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(self.name + ",", self.age, "years old.")
class Employee:
def __init__(self, salary):
self.salary = salary
def showSalary(self):
print("salary:", self.salary)
class Teacher(Person, Employee):
def __init__(self, name, age, salary):
super().__init__(name, age)
self.salary = salary
# create an object of Teacher with it's __init__() function
john = Teacher("John", 35, "5000000")
# call method inherits from Person
john.info()
# call method inherits from Employee
john.showSalary()
Kết quả
John, 35 years old.
salary: 5000000
Trong ví dụ trên, chúng sử dụng câu lệnh super().__init__(name, age)
để gọi hàm khởi tạo của lớp cha Person. Sau đó, chúng ta thêm thuộc tính salary cho các object của Teacher.