11.1 Thứ tự giải quyết phương thức
Thứ tự giải quyết phương thức (Method Resolution Order, MRO) xác định thứ tự mà Python tìm kiếm các phương thức và thuộc tính trong hệ thống phân cấp lớp. Điều này đặc biệt quan trọng khi làm việc với kế thừa đa lớp, khi một lớp có thể kế thừa các thuộc tính và phương thức từ nhiều lớp cha.
Nói một cách đơn giản, có một thứ tự cố định nghiêm ngặt (hoặc, đúng hơn, một thuật toán), theo đó Python đi qua cây kế thừa của các lớp. Thuật toán này đảm bảo một thứ tự tìm kiếm phương thức chính xác, có thể được mô tả như sau:
Thuật toán C3-linearization
Thuật toán C3-linearization xác định MRO thông qua việc kết hợp:
- Bản thân lớp.
- Danh sách các lớp cha theo thứ tự liệt kê của chúng.
- MRO của các lớp cha theo thứ tự tương tự.
Quy tắc của thuật toán C3-linearization
- Duy trì thứ tự phương thức địa phương: nếu lớp
Ađược liệt kê trước lớpB, tất cả các phương thức của lớpAphải được xem xét trước các phương thức của lớpB. - Duy trì thứ tự trong các lớp cha: nếu lớp
Alà lớp cha của lớpB, tất cả các phương thức của lớpAphải được xem xét trước các phương thức của lớpB. - Xem xét thứ tự kế thừa: nếu lớp
Clà lớp cha của hai hoặc nhiều lớp, thứ tự phương thức của lớpCphải được duy trì trong MRO của tất cả các lớp này.
Các bước của thuật toán:
Bước 1. Bắt đầu với lớp hiện tại:
Luôn bắt đầu với lớp hiện tại nơi mà phương thức được gọi.
Bước 2. Thêm các lớp cơ bản theo thứ tự liệt kê:
Sau lớp hiện tại, kiểm tra các lớp cơ bản theo thứ tự mà chúng được liệt kê khi kế thừa.
Bước 3. Duyệt qua các lớp cha:
Tìm kiếm các thuộc tính và phương thức ở đó.
Bước 4. Kết hợp MRO của các lớp cha:
Nếu một lớp cơ bản được kế thừa qua nhiều đường dẫn, nó chỉ được kiểm tra một lần và theo đúng thứ tự (tất cả các lần khác nó sẽ bị bỏ qua).
Đối với những ai đã quen thuộc với chủ đề "Thuật toán và cấu trúc dữ liệu", đây là tìm kiếm theo chiều sâu chứ không phải tìm kiếm theo chiều rộng.
11.2 Kiểm tra MRO
Trong Python, bạn có thể kiểm tra thứ tự tìm kiếm phương thức và thuộc tính của lớp, bằng cách sử dụng thuộc tính __mro__ hoặc hàm mro().
Ví dụ:
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
def method(self):
print("D")
# Kiểm tra MRO
print(D.__mro__)
Kết quả sẽ là:
(<class '__main__.D'>,
<class '__main__.B'>,
<class '__main__.C'>,
<class '__main__.A'>,
<class 'object'>)
Điều này cho thấy thứ tự mà Python sẽ tìm kiếm phương thức và thuộc tính:
D: Python trước hết kiểm tra phương thức trong lớpD.-
B: Sau đó Python kiểm tra phương thức trong lớpB(lớp cha đầu tiên). -
C: Nếu phương thức không được tìm thấy trong lớpB, Python kiểm tra phương thức trong lớpC(lớp cha thứ hai). -
A: Nếu phương thức không được tìm thấy trong các lớpBvàC, Python kiểm tra phương thức trong lớpA. -
object: Cuối cùng, Python kiểm tra phương thức trong lớp cơ bảnobject.
11.3 Sử dụng super() với MRO
Hàm super() tuân theo MRO để gọi các phương thức của lớp cha theo đúng thứ tự. Hãy xem một ví dụ về việc sử dụng
super():
class A:
def method(self):
print("A")
super().method()
class B(A):
def method(self):
print("B")
super().method()
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
def method(self):
print("D")
super().method()
d = D()
d.method()
Kết quả sẽ là:
D
B
C
A
Thứ tự duyệt qua (MRO)
1. Gọi phương thức method của lớp D:
- Python trước hết kiểm tra phương thức trong lớp
Dvà tìm thấy nó ở đó. - Phương thức
D.method()được thực thi và in ra"D". - Sau đó
super().method()được gọi, tuân theo MRO để gọi phương thức tiếp theo.
2. Gọi phương thức method của lớp B:
- Theo MRO, lớp tiếp theo sau
DlàB. - Phương thức
B.method()được thực thi và in ra"B". - Sau đó
super().method()được gọi, tuân theo MRO để gọi phương thức tiếp theo.
3. Gọi phương thức method của lớp C:
- Lớp tiếp theo trong MRO sau
BlàC. - Phương thức
C.method()được thực thi và in ra"C". - Sau đó
super().method()được gọi, tuân theo MRO để gọi phương thức tiếp theo.
4. Gọi phương thức method của lớp A:
- Lớp tiếp theo trong MRO sau
ClàA. - Phương thức
A.method()được thực thi và in ra"A". - Sau đó
super().method()được gọi, nhưng vìAkhông có các phương thứcmethodnào khác (ngoàiobject), cuộc gọi kết thúc mà không có hành động nào thêm.
GO TO FULL VERSION