18/09/2018, 16:27

Hiểu về thừa kế lớp trong Python 3

Giới thiệu Lập trình hướng đối tượng tạo ra các mẫu mã có thể tái sử dụng để hạn chế sự dư thừa trong các dự án phát triển. Một cách mà lập trình hướng đối tượng đạt được mã có thể tái chế là thông qua kế thừa, khi một lớp con có thể tận dụng mã từ một lớp cơ sở khác. Hướng dẫn này sẽ đi qua ...

Giới thiệu

Lập trình hướng đối tượng tạo ra các mẫu mã có thể tái sử dụng để hạn chế sự dư thừa trong các dự án phát triển. Một cách mà lập trình hướng đối tượng đạt được mã có thể tái chế là thông qua kế thừa, khi một lớp con có thể tận dụng mã từ một lớp cơ sở khác.

Hướng dẫn này sẽ đi qua một số khía cạnh chính của kế thừa trong Python, bao gồm cách các lớp cha và các lớp con làm việc, cách ghi đè các phương thức và các thuộc tính, cách sử dụng super() và cách sử dụng đa thừa kế.

Thừa kế là gì?

Di sản là khi một lớp sử dụng mã được xây dựng trong lớp khác. Nếu chúng ta nghĩ về sự kế thừa về mặt sinh học, chúng ta có thể nghĩ về một đứa trẻ thừa kế những đặc điểm nhất định từ cha mẹ chúng. Đó là, một đứa trẻ có thể thừa hưởng chiều cao hoặc màu mắt của cha mẹ. Trẻ em cũng có thể chia sẻ cùng họ với cha mẹ.

Các lớp được gọi là lớp trẻ em hoặc là lớp con kế thừa các phương thức và biến từ lớp cha hoặc là lớp cơ sở.

Chúng ta có thể nghĩ đến một lớp cha được gọi là Parent cái đó có biến lớp cho last_name, heightvà eye_color rằng lớp trẻ em Child sẽ kế thừa từ Parent.

Bởi vì Child lớp con được kế thừa từ Parent lớp cơ sở, Child lớp có thể sử dụng lại mã của Parent, cho phép lập trình viên sử dụng ít dòng mã hơn và giảm dự phòng.

Lớp học dành cho cha mẹ

Các lớp cha hoặc các lớp cơ sở tạo ra một khuôn mẫu trong đó các lớp con hoặc lớp con có thể dựa trên đó. Các lớp cha mẹ cho phép chúng ta tạo các lớp con thông qua kế thừa mà không phải viết lại mã giống nhau mỗi lần. Bất kỳ lớp nào cũng có thể được tạo thành một lớp cha, vì vậy chúng là các lớp đầy đủ chức năng theo đúng nghĩa của chúng chứ không phải chỉ các khuôn mẫu.

Giả sử chúng ta có một vị tướng Bank_account lớp cha mẹ có Personal_account và Business_account các lớp trẻ em. Nhiều phương thức giữa tài khoản cá nhân và tài khoản doanh nghiệp sẽ giống nhau, chẳng hạn như phương thức rút tiền và gửi tiền, vì vậy, các phương thức này có thể thuộc về cấp độ gốc của Bank_account. Các Business_account phân lớp sẽ có phương pháp cụ thể cho nó, bao gồm cả một cách để thu thập hồ sơ kinh doanh và các hình thức, cũng như một employee_identification_number biến.

Tương tự, một Animal lớp học có thể có eating() và sleeping() phương pháp và Snake phân lớp có thể bao gồm riêng của nó hissing() và slithering() phương pháp.

Hãy tạo một Fish lớp cha mà chúng ta sẽ sử dụng sau này để xây dựng các loại cá như các lớp con của nó. Từng loài cá này đều có tên và họ cùng với đặc điểm.

Chúng tôi sẽ tạo một tệp mới có tên fish.py và bắt đầu với [__init__() phương thức khởi tạo](https://www.codehub.vn/tim-kiem?q=init()%20ph%C6%B0%C6%A1ng%20th%E1%BB%A9c%20kh%E1%BB%9Fi%20t%E1%BA%A1o&utm_medium=codehub.vn&utm_source=www.digitalocean.com&utm_campaign=guest_content&utm_term=https://www.digitalocean.com/community/tutorials/understanding-class-inheritance-in-python-3), mà chúng tôi sẽ điền first_name và last_name biến lớp cho mỗi biến Fish đối tượng hoặc phân lớp.

fish.py

class Fish:
    def __init__(self, first_name, last_name="Fish"):
        self.first_name = first_name
        self.last_name = last_name

Chúng tôi đã khởi tạo last_name biến với chuỗi "Fish" bởi vì chúng ta biết rằng hầu hết cá sẽ có điều này như họ của họ.

Letâ € ™ s cũng thêm một số phương pháp khác:

fish.py

class Fish:
    def __init__(self, first_name, last_name="Fish"):
        self.first_name = first_name
        self.last_name = last_name

    def swim(self):
        print("The fish is swimming.")

    def swim_backwards(self):
        print("The fish can swim backwards.")

Chúng tôi đã thêm các phương pháp swim() và swim_backwards() đến Fish lớp, để mọi lớp con cũng sẽ có thể sử dụng các phương thức này.

Vì hầu hết cá chúng ta sẽ tạo ra được coi là cá xương (như trong họ có một bộ xương làm bằng xương) hơn là cá sụn (như trong chúng có bộ xương được làm từ sụn), chúng ta có thể thêm một vài thuộc tính vào __init__() phương pháp:

fish.py

class Fish:
    def __init__(self, first_name, last_name="Fish",
                 skeleton="bone", eyelids=False):
        self.first_name = first_name
        self.last_name = last_name
        self.skeleton = skeleton
        self.eyelids = eyelids

    def swim(self):
        print("The fish is swimming.")

    def swim_backwards(self):
        print("The fish can swim backwards.")

Xây dựng một lớp cha mẹ theo cùng phương pháp như xây dựng bất kỳ lớp nào khác, ngoại trừ chúng ta đang nghĩ về những phương thức mà các lớp con sẽ có thể sử dụng khi chúng ta tạo ra chúng.

Lớp học trẻ em

Lớp con hoặc lớp con là các lớp sẽ kế thừa từ lớp cha. Điều đó có nghĩa là mỗi lớp con sẽ có thể sử dụng các phương thức và các biến của lớp cha.

Ví dụ, một Goldfish lớp con phân lớp Fish lớp học sẽ có thể sử dụng swim() phương thức được khai báo trong Fish mà không cần phải khai báo.

Chúng ta có thể nghĩ về từng lớp con như là một lớp của lớp cha. Đó là, nếu chúng ta có một lớp con được gọi là Rhombus và một lớp cha được gọi là Parallelogram, chúng ta có thể nói rằng Rhombus là một Parallelogram, giống như một Goldfish là một Fish.

Dòng đầu tiên của một lớp con trông hơi khác một chút so với các lớp không phải con khi bạn phải chuyển lớp cha vào lớp con làm tham số:

class Trout(Fish):

Các Trout lớp là một đứa trẻ của Fish lớp học. Chúng tôi biết điều này vì sự bao gồm từ Fish trong dấu ngoặc đơn.

Với các lớp con, chúng ta có thể chọn thêm các phương thức khác, ghi đè các phương thức cha mẹ hiện có hoặc chỉ chấp nhận các phương thức cha mẹ mặc định bằng pass từ khóa mà chúng tôi sẽ thực hiện trong trường hợp này:

fish.py

...
class Trout(Fish):
    pass

Bây giờ chúng ta có thể tạo Trout đối tượng mà không cần phải xác định bất kỳ phương thức bổ sung nào.

fish.py

...
class Trout(Fish):
    pass

terry = Trout("Terry")
print(terry.first_name + " " + terry.last_name)
print(terry.skeleton)
print(terry.eyelids)
terry.swim()
terry.swim_backwards()

Chúng tôi đã tạo Trout vật terry sử dụng từng phương pháp của Fish lớp mặc dù chúng tôi không xác định các phương thức đó trong Trout lớp trẻ em. Chúng tôi chỉ cần vượt qua giá trị của "Terry" đến first_name biến vì tất cả các biến khác đã được khởi tạo.

Khi chúng tôi chạy chương trình, chúng tôi sẽ nhận được kết quả sau:

OutputTerry Fish
bone
False
The fish is swimming.
The fish can swim backwards.

Tiếp theo, hãy tạo một lớp con khác bao gồm phương thức riêng của nó. Chúng tôi sẽ gọi lớp này Clownfishvà phương pháp đặc biệt của nó sẽ cho phép nó sống với hải quỳ:

fish.py

...
class Clownfish(Fish):

    def live_with_anemone(self):
        print("The clownfish is coexisting with sea anemone.")

Tiếp theo, hãy tạo một Clownfish để xem cách hoạt động của nó:

fish.py

...
casey = Clownfish("Casey")
print(casey.first_name + " " + casey.last_name)
casey.swim()
casey.live_with_anemone()

Khi chúng tôi chạy chương trình, chúng tôi sẽ nhận được kết quả sau:

OutputCasey Fish
The fish is swimming.
The clownfish is coexisting with sea anemone.

Đầu ra cho thấy rằng Clownfish vật casey có thể sử dụng Fish phương pháp __init__() và swim() cũng như phương pháp lớp con của nó live_with_anemone().

Nếu chúng ta cố gắng sử dụng live_with_anemone() phương pháp trong một Trout đối tượng, chúng tôi sẽ nhận được lỗi:

Outputterry.live_with_anemone()
AttributeError: 'Trout' object has no attribute 'live_with_anemone'

Điều này là do phương pháp live_with_anemone() chỉ thuộc về Clownfish lớp trẻ em, và không phải là Fish lớp cha.

Các lớp con kế thừa các phương thức của lớp cha nó thuộc về, vì vậy mỗi lớp con có thể sử dụng các phương thức đó trong các chương trình.

Ghi đè các phương thức dành cho cha mẹ

Cho đến nay, chúng tôi đã xem xét các lớp con Trout đã sử dụng pass từ khóa để kế thừa tất cả các lớp cha Fish hành vi và một lớp học trẻ khác Clownfish thừa hưởng tất cả các hành vi của lớp cha và cũng tạo ra phương thức duy nhất của riêng nó, cụ thể cho lớp con. Đôi khi, tuy nhiên, chúng tôi sẽ muốn sử dụng một số hành vi của lớp cha mẹ nhưng không phải tất cả chúng. Khi chúng ta thay đổi phương thức lớp cha, chúng ta ghi đè chúng.

Khi xây dựng các lớp cha và con, điều quan trọng là phải giữ thiết kế chương trình trong tâm trí để ghi đè không tạo ra mã không cần thiết hoặc dự phòng.

Chúng tôi sẽ tạo Shark lớp con của Fish lớp cha. Bởi vì chúng tôi đã tạo Fish với ý tưởng rằng chúng tôi sẽ tạo ra cá xương chủ yếu, chúng ta sẽ phải điều chỉnh cho Shark lớp mà thay vào đó là một con cá sụn. Về mặt thiết kế chương trình, nếu chúng tôi có nhiều hơn một loài cá không có xương, chúng tôi rất có thể muốn tạo ra các lớp riêng biệt cho từng loại cá này.

Cá mập, không giống như cá xương, có bộ xương làm bằng sụn thay vì xương. Họ cũng có mí mắt và không thể bơi ngược. Cá mập có thể, tuy nhiên, cơ động mình ngược bằng cách chìm.

Theo đó, chúng tôi sẽ ghi đè __init__() phương thức khởi tạo và swim_backwards() phương pháp. Chúng tôi không cần sửa đổi swim() phương pháp kể từ khi cá mập là cá có thể bơi. Chúng ta hãy xem lớp trẻ này:

fish.py

...
class Shark(Fish):
    def __init__(self, first_name, last_name="Shark",
                 skeleton="cartilage", eyelids=True):
        self.first_name = first_name
        self.last_name = last_name
        self.skeleton = skeleton
        self.eyelids = eyelids

    def swim_backwards(self):
        print("The shark cannot swim backwards, but can sink backwards.")

Chúng tôi đã ghi đè các tham số được khởi tạo trong __init__() phương pháp, để last_name biến bây giờ được đặt bằng chuỗi "Shark", các skeleton biến bây giờ được đặt bằng chuỗi "cartilage", và eyelids biến bây giờ được đặt thành giá trị Boolean True. Mỗi thể hiện của lớp cũng có thể ghi đè các tham số này.

Phương pháp swim_backwards() bây giờ in một chuỗi khác với chuỗi trong Fish lớp cha mẹ vì cá mập không thể bơi ngược lại theo cách mà cá xương có thể.

Bây giờ chúng ta có thể tạo một thể hiện của Shark lớp trẻ em, mà vẫn sẽ sử dụng swim() phương pháp của Fish lớp cha mẹ:

fish.py

...
sammy = Shark("Sammy")
print(sammy.first_name + " " + sammy.last_name)
sammy.swim()
sammy.swim_backwards()
print(sammy.eyelids)
print(sammy.skeleton)

Khi chúng tôi chạy mã này, chúng tôi sẽ nhận được kết quả sau:

OutputSammy Shark
The fish is swimming.
The shark cannot swim backwards, but can sink backwards.
True
cartilage

Các Shark lớp trẻ vượt qua thành công __init__() và swim_backwards() phương pháp của Fish lớp cha mẹ, trong khi cũng kế thừa swim() phương thức của lớp cha.

Khi sẽ có một số lượng hạn chế các lớp con khác biệt hơn các lớp con khác, việc ghi đè các phương thức lớp cha có thể chứng minh là hữu ích.

Các super() Chức năng

Với super() , bạn có thể truy cập vào các phương thức kế thừa đã được ghi đè trong một đối tượng lớp.

Khi chúng ta sử dụng super() chức năng, chúng ta đang gọi một phương thức cha mẹ vào một phương thức con để sử dụng nó. Ví dụ, chúng ta có thể muốn ghi đè lên một khía cạnh của phương thức cha với một số chức năng nhất định, nhưng sau đó gọi phần còn lại của phương thức gốc ban đầu để hoàn thành phương thức.

Trong một chương trình xếp loại học sinh, chúng tôi có thể muốn có một lớp con Weighted_grade kế thừa từ Grade lớp cha. Trong lớp trẻ em Weighted_grade, chúng tôi có thể muốn ghi đè calculate_grade() phương thức của lớp cha để bao gồm các chức năng để tính điểm trọng số, nhưng vẫn giữ phần còn lại của chức năng của lớp gốc. Bằng cách gọi super() chức năng chúng tôi sẽ có thể đạt được điều này.

Các super() chức năng được sử dụng phổ biến nhất trong __init__() vì đó là nơi bạn sẽ cần thêm một số tính duy nhất cho lớp con và sau đó hoàn tất khởi tạo từ cha mẹ.

Để xem cách hoạt động, hãy thay đổi Trout lớp trẻ em. Vì cá hồi thường là cá nước ngọt, hãy thêm water biến thành __init__() và đặt nó bằng chuỗi "freshwater", nhưng sau đó duy trì các biến và tham số của lớp cha:

fish.py

...
class Trout(Fish):
    def __init__(self, water = "freshwater"):
        self.water = water
        super().__init__(self)
...

Chúng tôi đã ghi đè __init__() phương pháp trong Trout lớp con, cung cấp một triển khai khác nhau của __init__() đã được xác định bởi lớp cha của nó Fish. Trong __init__() phương pháp của chúng tôi Trout lớp chúng ta đã gọi một cách rõ ràng __init__() phương pháp của Fish lớp học.

Bởi vì chúng tôi đã ghi đè phương pháp, chúng tôi không còn cần phải vượt qua first_name như một tham số Troutvà nếu chúng tôi chuyển một tham số, chúng tôi sẽ đặt lại freshwater thay thế. Do đó, chúng tôi sẽ khởi tạo first_name bằng cách gọi biến trong trường hợp đối tượng của chúng ta.

Bây giờ chúng ta có thể gọi các biến khởi tạo của lớp cha và cũng sử dụng biến con duy nhất. Hãy sử dụng điều này trong một trường hợp Trout:

fish.py

...
terry = Trout()

# Initialize first name
terry.first_name = "Terry"

# Use parent __init__() through super()
print(terry.first_name + " " + terry.last_name)
print(terry.eyelids)

# Use child __init__() override
print(terry.water)

# Use parent swim() method
terry.swim()

OutputTerry Fish
False
freshwater
The fish is swimming.

Đầu ra cho thấy rằng đối tượng terry của Trout lớp trẻ có thể sử dụng cả hai __init__() biến water trong khi cũng có thể gọi Fish cha mẹ __init__() biến của first_name, last_namevà eyelids.

Hàm Python tích hợp sẵn super() cho phép chúng ta sử dụng các phương thức lớp cha ngay cả khi ghi đè các khía cạnh nhất định của các phương thức đó trong các lớp con của chúng ta.

Thừa kế nhiều lần

Đa thừa kế là khi một lớp có thể kế thừa các thuộc tính và phương thức từ nhiều hơn một lớp cha. Điều này có thể cho phép các chương trình để giảm dự phòng, nhưng nó cũng có thể giới thiệu một số tiền nhất định của sự phức tạp cũng như sự mơ hồ, do đó, nó nên được thực hiện với suy nghĩ để thiết kế chương trình tổng thể.

Để hiển thị số lượng thừa kế hoạt động, hãy tạo Coral_reef lớp con hơn thừa kế từ một Coral lớp và một Sea_anemone lớp học. Chúng ta có thể tạo một phương thức trong mỗi và sau đó sử dụng pass từ khóa trong Coral_reef lớp trẻ em:

coral_reef.py

class Coral:

    def community(self):
        print("Coral lives in a community.")

class Anemone:

    def protect_clownfish(self):
        print("The anemone is protecting the clownfish.")

class CoralReef(Coral, Anemone):
    pass

Các Coral lớp có một phương thức được gọi là community() in một dòng và Anemone lớp có một phương thức được gọi là protect_clownfish() in một dòng khác. Sau đó, chúng tôi gọi cả hai lớp vào thừa kế tuple. Điều này có nghĩa rằng Coral được kế thừa từ hai lớp cha.

Bây giờ, hãy khởi tạo một Coral vật:

coral_reef.py

...
great_barrier = CoralReef()
great_barrier.community()
great_barrier.protect_clownfish()

Đối tượng great_barrier được đặt thành CoralReef và có thể sử dụng các phương thức trong cả hai lớp cha. Khi chúng tôi chạy chương trình, chúng tôi sẽ thấy kết quả sau:

OutputCoral lives in a community.
The anemone is protecting the clownfish.

Đầu ra cho thấy rằng các phương thức từ cả hai lớp cha được sử dụng hiệu quả trong lớp con.

Nhiều thừa kế cho phép chúng ta sử dụng mã từ nhiều hơn một lớp cha trong một lớp con. Nếu cùng một phương thức được định nghĩa trong nhiều phương thức cha, thì lớp con sẽ sử dụng phương thức của bố mẹ thứ nhất được khai báo trong danh sách tuple của nó.

Mặc dù nó có thể được sử dụng một cách hiệu quả, nhiều kế thừa phải được thực hiện cẩn thận để các chương trình của chúng ta không trở nên mơ hồ và khó khăn cho các lập trình viên khác hiểu được.

Phần kết luận

Hướng dẫn này đã đi qua việc xây dựng các lớp cha và các lớp con, ghi đè các phương thức cha mẹ và các thuộc tính trong các lớp con, bằng cách sử dụng super() và cho phép các lớp con kế thừa từ nhiều lớp cha.

Thừa kế trong mã hóa hướng đối tượng có thể cho phép tuân thủ nguyên tắc DRY (không lặp lại chính mình) về phát triển phần mềm, cho phép thực hiện nhiều hơn với ít mã và lặp lại. Thừa kế cũng buộc các lập trình viên phải suy nghĩ về cách họ đang thiết kế các chương trình mà họ đang tạo ra để đảm bảo rằng mã hiệu quả và rõ ràng.

0