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ớpA
phả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
A
là lớp cha của lớpB
, tất cả các phương thức của lớpA
phả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
C
là lớp cha của hai hoặc nhiều lớp, thứ tự phương thức của lớpC
phả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ớpB
và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
D
và 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
D
là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
B
là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
C
làA
. - Phương thức
A.method()
được thực thi và in ra"A"
. -
Sau đó
super().method()
được gọi, nhưng vìA
không có các phương thứcmethod
nà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