了解 Python 3 中的类继承

介绍

面向对象的编程创建可重复使用的代码模式,以减少开发项目的冗余。面向对象的编程实现可回收代码的一种方式是通过继承,其中一个子类可以从另一个基础类中利用代码。

本教程将探讨Python中继承的一些主要方面,包括父母类和儿童类的运作方式、方法和属性的排除方法、如何使用super()函数以及如何使用多重继承。

前提条件

如果您没有设置编程环境,您可以参考本地编程环境的安装和安装指南(https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3)或适用于您的操作系统(Ubuntu, CentOS, Debian 等)的编程环境(https://www.digitalocean.com/community/tutorial_collections/how-to-install-python-3-and-set-up-a-programming-environment)。

什么是继承?

继承性是指一个班级使用在另一个班级内构建的代码。如果我们从生物学上考虑继承性,我们可以想到一个从父母那里继承某些特征的孩子。

被称为 子类子类的类继承了从 父母类基类的方法和变量。

我们可以想象一个名为父母的父母类,其中有上一个名字,身高眼睛颜色的类变量(LINK0),而孩子的孩子类将从父母中继承。

由于孩子子类是从父母基础类继承的,所以孩子类可以重复使用父母的代码,允许程序员使用更少的代码行,并减少冗余。

家长班级

父母或基础类创建一个模式,可以基于孩子或子类。父母类允许我们通过继承创建儿童类,而无需每次重写相同的代码。

假设我们有一个通用的Bank_account家长类,其中有Personal_accountBusiness_account儿童类。个人和商业账户之间的许多方法将是相似的,例如提款和存款方法,所以这些方法可能属于Bank_account的家长类。

类似地,一个动物类可能有eating()sleeping()方法,而一个子类可能包括自己的hissing()slithering()方法。

让我们创建一个母类,我们以后将使用它来构建鱼类类作为其子类。

<$>[info] 信息: 要跟进本教程中的示例代码,请在本地系统上运行python3命令,打开Python交互壳。

我们将创建一个名为fish.py的新文件,并开始使用 __init__() 构建方法,我们将填充每个Fish对象或子类的first_namelast_name类变量。

1[label fish.py]
2class Fish:
3    def __init__(self, first_name, last_name="Fish"):
4        self.first_name = first_name
5        self.last_name = last_name

我们已经将我们的最后_名称变量初始化为字符串,因为我们知道大多数鱼都将这个作为他们的姓氏。

再加上一些其他方法:

 1[label fish.py]
 2class Fish:
 3    def __init__(self, first_name, last_name="Fish"):
 4        self.first_name = first_name
 5        self.last_name = last_name
 6
 7    def swim(self):
 8        print("The fish is swimming.")
 9
10    def swim_backwards(self):
11        print("The fish can swim backwards.")

我们已将方法 swim()swim_backwards() 添加到 Fish 类中,以便每个子类都能使用这些方法。

由于我们将创建的大多数鱼被认为是骨鱼(LINK0)(因为它们有骨骼),而不是骨骼鱼(LINK1)(因为它们有骨骼骨骼),我们可以为__init__()方法添加一些其他属性:

 1[label fish.py]
 2class Fish:
 3    def __init__(self, first_name, last_name="Fish",
 4                 skeleton="bone", eyelids=False):
 5        self.first_name = first_name
 6        self.last_name = last_name
 7        self.skeleton = skeleton
 8        self.eyelids = eyelids
 9
10    def swim(self):
11        print("The fish is swimming.")
12
13    def swim_backwards(self):
14        print("The fish can swim backwards.")

构建一个家长类遵循与构建任何其他类相同的方法,但我们正在考虑儿童类一旦创建这些类,将能够使用哪些方法。

儿童班级

孩子或子类是将继承从父母类的类,这意味着每个孩子类将能够使用父母类的方法和变量。

例如,一个金鱼小班子将小班子分类,可以使用中声明的游泳()方法,而无需声明。

也就是说,如果我们有一个名为罗姆的儿童类和一个名为Parallelogram的父母类,我们可以说一个罗姆Parallelogram,就像一个金鱼一样。

儿童班的第一行看起来有点不同于非儿童班,因为您必须将家长班转入儿童班作为参数:

1class Trout(Fish):

真正的班级是鱼类班级的孩子,我们知道这一点是因为鱼类一词被包含在中。

对于儿童类,我们可以选择添加更多的方法,取代现有的家长方法,或使用pass关键字接受默认的家长方法,在这种情况下我们会这样做:

1[label fish.py]
2...
3class Trout(Fish):
4    pass

现在我们可以创建一个Trout对象,而不必定义任何额外的方法。

 1[label fish.py]
 2...
 3class Trout(Fish):
 4    pass
 5
 6terry = Trout("Terry")
 7print(terry.first_name + " " + terry.last_name)
 8print(terry.skeleton)
 9print(terry.eyelids)
10terry.swim()
11terry.swim_backwards()

我们创建了一个Trout对象terry,它使用了Fish类的每个方法,尽管我们没有在Trout儿童类中定义这些方法,我们只需要将Terry的值传递给first_name变量,因为所有其他变量都是初始化的。

当我们运行该程序时,我们将收到以下输出:

1[secondary_label Output]
2Terry Fish
3bone
4False
5The fish is swimming.
6The fish can swim backwards.

接下来,让我们创建另一个包括它自己的方法的儿童类,我们将这个类称为小丑鱼,它的特殊方法将允许它与海洋鱼一起生活:

1[label fish.py]
2...
3class Clownfish(Fish):
4
5    def live_with_anemone(self):
6        print("The clownfish is coexisting with sea anemone.")

接下来,让我们创建一个小丑鱼对象,看看它是如何工作的:

1[label fish.py]
2...
3casey = Clownfish("Casey")
4print(casey.first_name + " " + casey.last_name)
5casey.swim()
6casey.live_with_anemone()

当我们运行该程序时,我们将收到以下输出:

1[secondary_label Output]
2Casey Fish
3The fish is swimming.
4The clownfish is coexisting with sea anemone.

输出显示Clownfish对象casey能够使用Fish方法__init__()swim(),以及其子类方法live_with_anemone()

如果我们尝试在一个Trout对象中使用live_with_anemone()方法,我们会收到一个错误:

1[secondary_label Output]
2terry.live_with_anemone()
3AttributeError: 'Trout' object has no attribute 'live_with_anemone'

这是因为live_with_anemone()方法只属于Clownfish儿童类,而不是Fish家长类。

孩子们的班级继承了他们所属的家长班级的方法,所以每个孩子班级可以在程序中使用这些方法。

超越父母的方法

到目前为止,我们已经研究了使用pass关键字来继承所有父母类行为的Trout儿童类,还有另一个孩子类Clownfish,继承了所有父母类行为,并创建了自己的独特方法,是针对儿童类的。

在构建家长和儿童课程时,重要的是要记住程序设计,以免产生不必要或多余的代码。

我们将创建一个母类的儿童类,因为我们创建了类,因为我们认为我们将主要创建骨鱼,我们将不得不对类进行调整,而这是一种鱼。

鲨鱼与骨头鱼不同的是,它们有骨骼而不是骨骼的骨骼,它们也有眉毛,无法向后游泳,但是鲨鱼可以通过沉没来向后操纵。

鉴于此,我们将超越__init__()构造方法和swim_backwards()方法,我们不需要修改swim()方法,因为鲨鱼是可以游泳的鱼类。

 1[label fish.py]
 2...
 3class Shark(Fish):
 4    def __init__(self, first_name, last_name="Shark",
 5                 skeleton="cartilage", eyelids=True):
 6        self.first_name = first_name
 7        self.last_name = last_name
 8        self.skeleton = skeleton
 9        self.eyelids = eyelids
10
11    def swim_backwards(self):
12        print("The shark cannot swim backwards, but can sink backwards.")

我们在__init__()方法中重定义了初始化参数,所以 Last_name变量现在被设置为等于字符串Shark,Skeleton变量现在被设置为等于字符串Cartilage,而Eyelids变量现在被设置为布尔值True

该方法swim_backwards()现在打印了与母类中的一个不同的字符串,因为鲨鱼不能像骨鱼那样向后游泳。

现在我们可以创建一个Shark儿童类的实例,它仍然会使用Fish母类的swim()方法:

1[label fish.py]
2...
3sammy = Shark("Sammy")
4print(sammy.first_name + " " + sammy.last_name)
5sammy.swim()
6sammy.swim_backwards()
7print(sammy.eyelids)
8print(sammy.skeleton)

当我们运行此代码时,我们将收到以下输出:

1[secondary_label Output]
2Sammy Shark
3The fish is swimming.
4The shark cannot swim backwards, but can sink backwards.
5True
6cartilage

Shark儿童类成功地超越了Fish母类的__init__()swim_backwards()方法,同时也继承了母类的swim()方法。

当有有限数量的儿童班级比其他班级更独特时,优越的家长班级方法可以证明是有用的。

超级( )函数

使用super()函数,您可以访问在类对象中被重写的继承方法。

当我们使用super()函数时,我们将一个家长方法调用到一个孩子的方法中,以便使用它,例如,我们可能希望将父母方法的一个方面与某些功能相提并论,然后调用其余的原始家长方法来完成该方法。

在一个评级学生的程序中,我们可能希望有一个孩子类重量级级别的母级级继承。在儿童类重量级中,我们可能希望将母级方法calculate_grade()取代,以便包括计算重量级的功能,但仍然保留原始类的其余功能性。

super()函数最常用在__init__()方法中,因为这就是你最有可能需要将某些独特性添加到孩子的类,然后从父母开始完成。

由于鱼通常是淡水鱼,让我们将一个变量添加到__init__()方法中,并将其设置为淡水字符串,但然后保持其余的母类变量和参数:

1[label fish.py]
2...
3class Trout(Fish):
4    def __init__(self, water = "freshwater"):
5        self.water = water
6        super().__init__(self)
7...

我们在Trout儿童类中推翻了__init__()方法,提供了已由其母类Fish定义的__init__()的不同实现,在我们Trout类的__init__()方法中,我们明确提到了Fish类的__init__()方法。

由于我们已经翻译了方法,我们不再需要将first_name作为参数传输到Trout,如果我们通过了参数,我们会重新设置淡水

现在我们可以调用父母类的初始化变量,并使用独特的子变量。

 1[label fish.py]
 2...
 3terry = Trout()
 4
 5# Initialize first name
 6terry.first_name = "Terry"
 7
 8# Use parent __init__() through super()
 9print(terry.first_name + " " + terry.last_name)
10print(terry.eyelids)
11
12# Use child __init__() override
13print(terry.water)
14
15# Use parent swim() method
16terry.swim()
1[secondary_label Output]
2Terry Fish
3False
4freshwater
5The fish is swimming.

输出显示,Trout儿童类的对象terry能够使用儿童特定的__init__()变量,同时还可以调用父母__init__()变量first_name,last_nameeyelids

内置的Python函数super()允许我们使用家长类方法,即使在我们的孩子类中优先考虑这些方法的某些方面。

多重遗产

多重继承是指一个类可以继承来自多个母类的属性和方法,这可以允许程序减少冗余,但也可以引入一定程度的复杂性和模糊性,因此应该考虑到整体的程序设计。

为了展示多重继承是如何工作的,让我们创建一个Coral_reef儿童类别,而不是继承从一个Coral类别和一个Sea_anemone类别。

 1[label coral_reef.py]
 2class Coral:
 3
 4    def community(self):
 5        print("Coral lives in a community.")
 6
 7class Anemone:
 8
 9    def protect_clownfish(self):
10        print("The anemone is protecting the clownfish.")
11
12class CoralReef(Coral, Anemone):
13    pass

Coral类有一个名为 community() 的方法,它打印出一行,而 Anemone 类有一个名为 protect_clownfish() 的方法,它打印出另一行。

现在让我们实例化一个CoralReef对象:

1[label coral_reef.py]
2...
3great_barrier = CoralReef()
4great_barrier.community()
5great_barrier.protect_clownfish()

对象 great_barrier 被设置为 CoralReef 对象,并且可以在两个母类中使用这些方法. 当我们运行程序时,我们会看到以下输出:

1[secondary_label Output]
2Coral lives in a community.
3The anemone is protecting the clownfish.

结果表明,在儿童类中有效地使用了来自父母两类的方法。

多重继承允许我们在儿童类中使用一个以上的家长类的代码. 如果在多个家长方法中定义了相同的方法,儿童类将使用其列表中声明的第一个家长的方法。

虽然它可以被有效地使用,但多重继承应该仔细地进行,以便我们的程序不会变得模糊和其他程序员难以理解。

结论

本教程通过构建家长类和儿童类,在儿童类中优先考虑家长方法和属性,使用super()函数,并允许儿童类从多个家长类继承。

对象导向编码的继承可以允许遵守软件开发的DRY(不要重复)原则,允许用更少的代码和重复来完成更多工作。

Published At
Categories with 技术
comments powered by Disqus