Python hỗ trợ đa kế thừa như thế nào?

Các ngôn ngữ lập trình hướng đối tượng cấp cao như C# và Java không hỗ trợ đa kế thừa. Vì một lý do tốt quá. Nó ngăn chặn sự mơ hồ và tạo điều kiện cho việc gõ và đóng gói mạnh mẽ. Điều này cuối cùng dẫn đến mã sạch hơn và cấu trúc mô hình lớp/đối tượng ít khó hiểu hơn. Tuy nhiên, đa kế thừa cho phép các mẫu thiết kế linh hoạt hơn.  

Nó cũng có thể rất thuận tiện, cho phép các lập trình viên viết ít mã hơn và đẩy nhanh quá trình sản xuất. Tuy nhiên, khi bạn đang làm việc với các ứng dụng lớn, phức tạp (đặc biệt là những ứng dụng liên quan đến máy học), tính đa kế thừa có thể khó quản lý và bảo trì. Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách sử dụng đa thừa kế và làm cho nó bền vững

 

Đa kế thừa trong Python

 
Giống như C++, các lớp trong Python có thể được bắt nguồn từ nhiều lớp (thay vì chỉ một lớp). Lớp dẫn xuất kế thừa tất cả các tính năng của lớp cha (các biến và phương thức/hàm). Trên thực tế, việc định nghĩa một lớp có nhiều kế thừa thực sự không khác gì định nghĩa một lớp có một kế thừa. Dưới đây là một ví dụ để giúp minh họa thực tế này.   

 

Thừa kế đơn

 

class Parent:
    pass
class Child(Parent):
    pass


 

Đa thừa kế

 

class Parent1:
    pass
class Parent2:
    pass
class Child(Parent1, Parent2):
    pass


Vì lớp Con được dẫn xuất từ ​​hai lớp nên nó thừa hưởng các tính năng của cả hai lớp. Tất nhiên, bạn có thể kế thừa từ nhiều hơn hai lớp. Về mặt lý thuyết, không có giới hạn về số lớp mà bạn có thể sử dụng để xây dựng một lớp con.  

 

Kế thừa đa cấp

 
Giống như hầu hết các ngôn ngữ lập trình hướng đối tượng, Python có tính năng kế thừa đa cấp. Nếu lớp con của bạn được dẫn xuất từ ​​một lớp kế thừa các tính năng của nó từ một lớp khác, nó sẽ kế thừa tất cả các tính năng từ cả hai lớp

Python hỗ trợ đa kế thừa như thế nào?

sơ đồ 1
 

Như bạn có thể thấy từ sơ đồ trên, lớp thấp nhất trong hệ thống phân cấp lớp kế thừa tất cả các hàm/phương thức và biến từ hai lớp trên nó. Kết hợp với đa kế thừa, kế thừa đa cấp có thể cung cấp cho nhà phát triển các tùy chọn thiết kế linh hoạt. Họ cũng có thể tăng khả năng sử dụng lại mã. Tuy nhiên, chúng cũng có thể làm cho cấu trúc lớp phức tạp hơn

Chẳng hạn, nếu bạn đang sử dụng một tính năng (phương thức hoặc biến) có tên và cấu trúc giống hệt với một phương thức trong lớp cha hoặc lớp cơ sở, thì phương thức nào sẽ được gọi đầu tiên từ lớp con? . Python xử lý việc này như thế nào?

 

Thứ tự giải quyết phương pháp

 
Một lần nữa, như với hầu hết các ngôn ngữ lập trình hướng đối tượng, tất cả các lớp đều bắt nguồn từ một lớp duy nhất. Trong Python, đây được gọi là lớp đối tượng. Do đó, theo mặc định, mọi lớp bạn tạo đều là lớp con/lớp dẫn xuất/lớp con của lớp Đối tượng. Khi chúng ta cố gắng hiểu thứ tự gọi của các phương thức và biến trong Python, bạn phải luôn ghi nhớ thông tin này khi cố gắng vạch ra thứ tự gọi phương thức của chương trình.  

Một lần nữa, vì Python có tính năng kế thừa đa cấp và đa cấp, nên nó yêu cầu một hệ thống giải quyết các xung đột thừa kế trong đó cùng một thuộc tính có các định nghĩa khác nhau trong nhiều lớp cơ sở/siêu lớp. Cách tốt nhất để hiểu điều này là xem một ví dụ mã hóa

class A:
    num = 10
class B(A):
    pass
class C(A):
    num = 1
class D(B,C):
    pass


Lớp A kế thừa từ lớp Object (giống như tất cả các lớp trong Python). Lớp B sử dụng Lớp A làm lớp cơ sở. Nó không có tính năng của riêng mình. Tuy nhiên, nó kế thừa thuộc tính đối tượng lớp num từ lớp A (cũng như các tính năng khác từ lớp đối tượng)

Lớp C cũng là một lớp con của Lớp A và định nghĩa một thuộc tính đối tượng lớp có chung tên với một thuộc tính được xác định trong Lớp A. Lớp D kế thừa từ D và C nhưng không xác định thuộc tính nào của riêng nó. Mối quan hệ trông rất giống thế này.  

Python hỗ trợ đa kế thừa như thế nào?

sơ đồ 2
 

Sơ đồ trên minh họa cái được gọi là vấn đề kim cương (hay kim cương chết chóc) trong điện toán. Thứ tự giải quyết phương pháp (MRO) là một quy tắc để giải quyết vấn đề này. Nó sử dụng MRO để tổ chức các lời gọi phương thức và thuộc tính

Ghi chú. Sơ đồ 2 không phải là biểu đồ phụ thuộc nghiêm ngặt. Thay vào đó, nó minh họa dòng kế thừa.    

Hãy thêm một phương thức tham chiếu thuộc tính num được kế thừa bởi lớp D. Phương thức này nên được gọi bên ngoài tất cả các lớp.  

print(D.num)


Nếu chúng tôi chạy nó cùng với mã trước đó, nó sẽ trả về số 1 làm đầu ra của chúng tôi. Bạn có thể nói rằng đó là giá trị của thuộc tính num trong Lớp C. Nhưng tại sao nó lại trả về thuộc tính và giá trị cụ thể đó?

MRO tiến hành tìm kiếm thuộc tính bắt đầu từ lớp hiện tại trước khi chuyển sang các lớp cha. Nếu có nhiều lớp cha, MRO sẽ tìm từ trái sang phải. Vì vậy, trong ví dụ của chúng tôi, việc tìm kiếm sẽ được tiến hành như sau. D -> B -> C -> A -> lớp đối tượng

Mặc dù sẽ rất hữu ích nếu bạn xây dựng tỉ mỉ biểu đồ phụ thuộc hoặc sơ đồ lớp giống như khi bạn lập kế hoạch kinh doanh, nhưng điều đó là không cần thiết. Bạn chỉ cần sử dụng phương thức mro() tích hợp sẵn của Python để tìm thứ tự giải quyết

Nếu chúng ta chạy phương thức mro() trên lớp D và in ra kết quả (i. e. in(D. mro())), bảng điều khiển sẽ hiển thị các kết quả này.  

[, , , , ]

Một lần nữa, nó có thể được dịch là. D -> B -> C -> A -> lớp đối tượng. Tuy nhiên, điều này dễ hiểu đối với các bài toán kim cương đơn giản như bài toán trên. Tuy nhiên, điều gì sẽ xảy ra nếu bạn nhận được.  

Python hỗ trợ đa kế thừa như thế nào?

Sơ đồ 3 (Nguồn. Giấy phép Wikimedia Commons (CC))
 

MRO sử dụng thuật toán có tên là Tìm kiếm theo chiều sâu để giải quyết tất cả các lệnh gọi phương thức và thuộc tính. Phiên bản Python mới nhất đã sửa đổi cách hoạt động của MRO. Không chắc liệu các quy tắc đường dẫn lớp của Python và MRO cũng sẽ được tinh chỉnh trong các phiên bản tương lai hay không. Do đó, nhiều nhà phát triển (bao gồm cả) bản thân tôi khuyên bạn nên tránh xa việc xây dựng các chương trình hoặc thuật toán có quá nhiều kế thừa đa cấp và đa cấp.  

Nếu bạn đang viết mã có cấu trúc phụ thuộc tương tự như sơ đồ trên, có thể lập luận rằng bạn đã viết mã kém. Về cơ bản, các ví dụ trong hướng dẫn này hầu như không có bất kỳ mã chức năng thực sự nào giữa các lớp. Tuy nhiên, các cấu trúc vẫn còn khó hiểu. Bây giờ, hãy tưởng tượng nếu bạn định nghĩa các phương thức và thuộc tính có cấu trúc giống hệt nhau và bắt đầu gọi chúng

 

Phần kết luận

 
Mặc dù MRO tồn tại và mọi nhà phát triển Python nên hiểu nó, nhưng mã của bạn không được có cấu trúc như Sơ đồ 3. Tuy nhiên, với tư cách là nhà phát triển (đặc biệt là người làm việc với máy học), bạn nhất định gặp phải cấu trúc lớp với các phụ thuộc phức tạp. Sử dụng phương thức mro() sẽ giúp bạn gỡ lỗi, gỡ rối và hiểu mã mà bạn có thể chưa viết.  

Ưu điểm chính của Python so với các ngôn ngữ lập trình OO khác là tính linh hoạt và tự do của nó. Tuy nhiên, tính lưu động của nó cũng có thể được coi là một nhược điểm. Có quá nhiều chỗ cho lỗi và mã không hiệu quả. Vì vậy, các lập trình viên, đặc biệt là những người làm việc với machine learning và deep learning, nên tìm hiểu và áp dụng các mẫu thiết kế tối ưu nhất. Tuy nhiên, để an toàn, bạn có thể chọn hoàn toàn tránh sử dụng đa kế thừa trong mã của mình. Tuy nhiên, nếu bạn quyết định thực hiện nó, hãy thực hiện nó một cách thận trọng và tiết kiệm

Đa kế thừa hoạt động như thế nào trong Python?

Một lớp có thể được bắt nguồn từ nhiều hơn một lớp cơ sở trong Python, tương tự như C++. Cái này gọi là đa thừa kế. Trong đa kế thừa, các tính năng của tất cả các lớp cơ sở được kế thừa vào lớp dẫn xuất . Cú pháp của đa thừa kế tương tự như đơn thừa kế.

Tại sao Python không hỗ trợ đa kế thừa?

Có, Python hỗ trợ đa kế thừa. Giống như C++, một lớp có thể được dẫn xuất từ ​​nhiều lớp cơ sở trong Python .

Python có hỗ trợ kế thừa nhiều lớp không?

Python hỗ trợ kế thừa từ nhiều lớp .

Python có hỗ trợ đa kế thừa Nó giải quyết vấn đề kim cương như thế nào?

Python không gặp sự cố này do thứ tự giải quyết phương pháp . Tóm lại, khi bạn kế thừa từ nhiều lớp, nếu tên phương thức của chúng xung đột, thì tên đầu tiên được ưu tiên. Vì chúng tôi đã chỉ định D(B, C) , B. do_thing được gọi trước C.