Bài viết đăng tại: https://programming.laptrinh.site
Các phiên bản 3.7 trở lên đã giới thiệu tính kế thừa lớp dữ liệu trong Python. Bài viết này giải thích rộng rãi về kế thừa đa cấp và cách sử dụng kế thừa lớp dữ liệu trong Python.
Nội dung
Kế thừa trong Python
Kế thừa lớp dữ liệu trong Python được sử dụng để lấy dữ liệu trong các lớp con từ lớp cha của nó, giúp giảm mã lặp lại và làm cho mã có thể tái sử dụng.
Hãy xem xét một ví dụ về thừa kế:
Chương trình nhập gói thư viện dataclass
để cho phép tạo các lớp được trang trí. Lớp đầu tiên được tạo ở đây là Parent
, lớp này có hai phương thức thành viên – name
chuỗi và số nguyên age
.
Sau đó, một lớp con của Parent
được tạo ở đây. Lớp Child
giới thiệu một phương thức thành viên mới – school
.
Một jack
đối tượng thể hiện được tạo cho lớp Parent
, lớp này chuyển hai đối số cho lớp. Một đối tượng thể hiện khác, jack_son
, được tạo cho lớp Child
.
Vì lớp Child
là lớp con của lớp Parent
, nên các thành viên dữ liệu có thể được dẫn xuất trong lớp Child
. Đây là đặc điểm chính của kế thừa lớp dữ liệu trong Python.
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
def print_name(self):
print(f"Name is '{self.name}' and age is= {self.age}")
@dataclass
class Child (Parent):
school: str
jack = Parent( 'Jack snr' , 35 )
jack_son = Child( 'Jack jnr' , 12 , school = 'havard' )
jack_son . print_name()
Đầu ra:
C:python38python.exe "C:/Users/Win 10/PycharmProjects/class inheritance/2.py"
Name is 'Jack jnr' and age is= 12
Process finished with exit code 0
Kế thừa đa cấp trong Python
Như chúng ta đã thấy cách hoạt động của tính kế thừa lớp dữ liệu trong Python, bây giờ chúng ta sẽ xem xét khái niệm kế thừa đa cấp. Nó là một kiểu thừa kế trong đó một lớp con được tạo ra từ một lớp cha được sử dụng làm cha cho lớp cháu tiếp theo.
Ví dụ dưới đây thể hiện tính kế thừa đa cấp ở dạng đơn giản:
lớp phụ huynh
Một lớp Parent
được tạo với một hàm tạo __init__
và một phương thức thành viên – print_method
. Hàm tạo in câu lệnh "Initialized in Parent"
để nó được hiển thị khi lớp được gọi từ lớp con của nó.
Hàm print_method
có tham số b
, được in khi phương thức này được gọi.
lớp con
Lớp Child
được dẫn xuất từ Lớp Parent
và in một câu lệnh bên trong hàm tạo của nó. super().__init__
đề cập đến lớp cơ sở với lớp con.
Chúng tôi sử dụng super()
để mọi kế thừa hợp tác tiềm năng được sử dụng bởi các lớp con sẽ gọi hàm lớp cha tiếp theo phù hợp trong Thứ tự giải quyết phương thức (MRO).
Phương thức thành viên print_method
bị quá tải và câu lệnh in sẽ in giá trị của b
. Ở đây cũng vậy, super()
đề cập đến phương thức thành viên của lớp cha của nó.
Lớp Cháu Lớn
Tại thời điểm này, chương trình chỉ soạn sẵn (mã lặp lại) cấu trúc để tạo một lớp kế thừa khác từ lớp Child
. Bên trong print_method
, giá trị của b
được tăng lên bằng cách sử dụng super()
.
Chức năng chính
Cuối cùng, hàm main
được tạo, hàm này tạo đối tượng ob
và được tạo thành một thể hiện của GrandChild()
. Cuối cùng, đối tượng ob
gọi print_method
.
Đây là cách các lớp đa cấp được xếp chồng lên nhau khi sử dụng kế thừa lớp dữ liệu trong Python.
class Parent:
def __init__(self):
print("Initialized in Parent")
def print_method(self, b):
print("Printing from class Parent:", b)
class Child(Parent):
def __init__(self):
print("Initialized in Child")
super().__init__()
def print_method(self, b):
print("Printing from class Child:", b)
super().print_method(b + 1)
class GrandChild(Child):
def __init__(self):
print("Initialized in Grand Child")
super () . __init__()
def print_method (self, b):
print ( "Printing from class Grand Child:" , b)
super () . print_method(b + 1 )
if __name__ == '__main__' :
ob = GrandChild()
ob . print_method( 10 )
Đầu ra:
C:python38python.exe "C:/Users/Win 10/PycharmProjects/class inheritance/3.py"
Initialized in Grand Child
Initialized in Child
Initialized in Parent
Printing from class Grand Child: 10
Printing from class Child: 11
Printing from class Parent: 12
Process finished with exit code 0
Hãy hiểu những gì mã làm ở đây:
Hàm main
chuyển giá trị 10
cho hàm print_method
của lớp Grand Child
. Theo MRO (Thứ tự giải quyết phương thức), trước tiên, chương trình thực thi lớp Grand Child
, in câu lệnh __init__
, sau đó chuyển sang lớp cha của nó – lớp Child
.
Lớp Child
và lớp Parent
in các câu lệnh __init__
của chúng theo MRO, sau đó trình biên dịch sẽ quay lại print_method
của lớp GrandChild
. Phương thức này in ra 10
(giá trị của b
) và sau đó sử dụng super()
để tăng giá trị của b
trong lớp cha của nó, Lớp Child
.
Sau đó, trình biên dịch chuyển sang print_method
của lớp Child
và in ra 11
. Sau đó, ở cấp độ cuối cùng của MRO là lớp Parent
in 12
.
Chương trình thoát vì không có lớp cha nào tồn tại trên lớp Parent
.
Như chúng ta đã hiểu cách thức hoạt động của tính kế thừa đa cấp trong kế thừa lớp dữ liệu trong Python, phần tiếp theo sẽ đề cập đến khái niệm kế thừa các thuộc tính từ lớp cha và cách sửa đổi nó.
Trộn các thuộc tính mặc định và không mặc định giữa lớp cơ sở và lớp con bằng cách sử dụng kế thừa lớp dữ liệu trong Python
Chúng ta đã thấy cách sử dụng một lớp con để truy cập các thành viên dữ liệu của lớp cha trong phần kế thừa lớp dữ liệu trong Python và cách hoạt động của tính kế thừa đa cấp. Bây giờ, một câu hỏi đặt ra nếu một lớp con có thể truy cập các thành viên dữ liệu của lớp cha của nó, liệu nó có thể thay đổi nó không?
Câu trả lời là có, nhưng nó phải tránh TypeErrors. Ví dụ, trong chương trình dưới đây, có hai lớp, một lớp Parent
và một lớp con Child
.
Lớp Parent
có ba thành viên dữ liệu – name
, age
và một biến bool ugly
được đặt là False
theo mặc định. Ba phương thức thành viên in name
, age
và id.
Bây giờ, bên trong lớp Child
được trang trí bắt nguồn từ Parent
, một thành viên dữ liệu mới được giới thiệu – school
. Với nó, lớp thay đổi thuộc tính của biến ugly
từ False
thành True
.
Hai đối tượng jack
dành cho Parent
và jack_son
dành cho Child
được tạo. Các đối tượng này chuyển các đối số cho các lớp của chúng và cả hai đối tượng đều gọi phương thức print_id
và in các chi tiết.
Một vấn đề lớn khi sử dụng phương thức này để thay đổi các giá trị mặc định của lớp cơ sở là nó gây ra TypeError.
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"ID: Name - {self.name}, age = {self.age}")
@dataclass
class Child(Parent):
school: str
ugly: bool = True
jack = Parent( 'jack snr' , 32 , ugly = True )
jack_son = Child( 'Mathew' , 14 , school = 'cambridge' , ugly = True )
jack . print_id()
jack_son . print_id()
Đầu ra:
raise TypeError(f'non-default argument {f.name!r} '
TypeError: non-default argument 'school' follows default argument
Lý do đằng sau lỗi này là trong kế thừa lớp dữ liệu trong Python, các thuộc tính không thể được sử dụng với giá trị mặc định trong lớp cơ sở và sau đó được sử dụng mà không có giá trị mặc định (thuộc tính vị trí) trong lớp con do cách các lớp dữ liệu kết hợp các thuộc tính.
Điều này là do các thuộc tính được hợp nhất từ đầu ở dưới cùng của MRO và xây dựng một danh sách các thuộc tính được sắp xếp theo thứ tự nhìn thấy trước, với các phần ghi đè vẫn ở vị trí ban đầu của chúng.
Với ugly
như mặc định, Parent
bắt đầu bằng name
, age
và ugly
, sau đó Child
thêm school
vào cuối danh sách đó (với ugly
đã có trong danh sách).
Điều này dẫn đến việc có name
, age
, ugly
và school
trong danh sách và vì school
không có giá trị mặc định nên hàm __init__
liệt kê kết quả dưới dạng tham số không chính xác.
Khi trình trang trí @dataclass
tạo một Lớp dữ liệu mới, nó sẽ tìm kiếm thông qua tất cả các lớp cơ sở của lớp trong MRO đảo ngược (bắt đầu từ đối tượng) và thêm các trường từ mỗi lớp cơ sở vào một ánh xạ các trường được sắp xếp theo thứ tự cho mỗi Lớp dữ liệu mà nó tìm thấy.
Sau đó, nó thêm các trường của nó vào ánh xạ có thứ tự sau khi tất cả các trường của lớp cơ sở đã được thêm vào. Ánh xạ các trường được sắp xếp theo thứ tự được tính toán kết hợp này sẽ được sử dụng bởi tất cả các phương thức đã tạo.
Do sự sắp xếp của các trường, các lớp dẫn xuất thay thế các lớp cơ sở.
Nếu một trường không có giá trị mặc định theo sau một trường có giá trị mặc định, TypeError sẽ được tạo. Điều này đúng cho dù nó xảy ra trong một lớp đơn lẻ hay do sự kế thừa của lớp.
Giải pháp thay thế đầu tiên để giải quyết vấn đề này là buộc các trường có giá trị mặc định ở vị trí sau trong thứ tự MRO bằng cách sử dụng các lớp cơ sở khác nhau. Tránh đặt các trường trực tiếp trên các lớp sẽ được sử dụng làm lớp cơ sở, như Parent
, bằng mọi giá.
Chương trình này có các lớp cơ sở với các trường và các trường không có giá trị mặc định được phân tách. Các lớp công khai xuất phát từ các lớp có base-with
và base-without
.
Các lớp con của lớp công khai đặt lớp cơ sở lên trước.
from dataclasses import dataclass
@dataclass
class _ParentBase:
name: str
age: int
@dataclass
class _ParentDefaultsBase:
ugly: bool = False
@dataclass
class _ChildBase(_ParentBase):
school: str
@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
ugly: bool = True
@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"ID: Name - {self.name}, age = {self.age}")
@dataclass
class Child (_ChildDefaultsBase, Parent, _ChildBase):
pass
Amit = Parent( 'Amit snr' , 32 , ugly = True )
Amit_son = Child( 'Amit jnr' , 12 , school = 'iit' , ugly = True )
Amit . print_id()
Amit_son . print_id()
Đầu ra:
C:python38python.exe "C:/main.py"
The Name is Amit snr and Amit snr is 32 year old
The Name is Amit jnr and Amit jnr is 12 year old
Process finished with exit code 0
MRO được tạo ở đây ưu tiên các trường không có giá trị mặc định so với các trường có giá trị mặc định bằng cách chia nhỏ các trường để tách các lớp cơ sở có trường without defaults
và with defaults
, đồng thời cẩn thận chọn thứ tự kế thừa. MRO của Child
là:
<class 'object'>
||
<class '__main__._ParentBase'>,
||
<class '__main__._ChildBase'>
||
<class '__main__._ParentDefaultsBase'>,
||
<class '__main__.Parent'>,
||
<class '__main__._ChildDefaultsBase'>,
||
<class '__main__._Child'>
Mặc dù Parent
không tạo bất kỳ trường mới nào, nhưng nó kế thừa các trường từ ParentDefaultsBase
và không được xếp sau cùng trong thứ tự liệt kê trường. Vì vậy, ChildDefaultsBase
cuối cùng được giữ lại để thực hiện đúng loại đơn đặt hàng.
Các quy tắc của lớp dữ liệu cũng được đáp ứng do các lớp ParentBase
và ChildBase
có các trường không có giá trị mặc định, xuất hiện trước ParentDefaultsBase
và ChildDefaultsBase
có các trường có giá trị mặc định.
Do đó, Child
vẫn là một lớp con của Parent
, trong khi các lớp Parent
và Child
có thứ tự trường chính xác:
__ Program Above __
print (signature(Parent))
print (signature(Child))
Đầu ra:
Phần kết luận
Bài viết này giải thích chi tiết về kế thừa lớp dữ liệu trong Python. Các khái niệm như lớp dữ liệu, lớp con, kế thừa đa cấp và trộn các thuộc tính từ lớp cơ sở sang lớp con được giải thích cặn kẽ.
URL Link
https://laptrinh.site/ke-thua-lop-du-lieu-trong-python/
Viết bởi Duy Mạnh. Đã đăng ký bản quyền tác giả tại Creativecommons và DMCA