Kế thừa lớp dữ liệu trong Python

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.

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 Parentjack_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 , ageugly , 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 , uglyschool 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-withbase-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 defaultswith 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 ParentBaseChildBase có các trường không có giá trị mặc định, xuất hiện trước ParentDefaultsBaseChildDefaultsBase 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 ParentChild có thứ tự trường chính xác:

 __ Program Above __
print (signature(Parent)) print (signature(Child))

Đầu ra:

Chức năng chữ ký

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