面向对象编程创建可重用的代码模式,以减少开发项目中的冗余。面向对象编程实现可回收代码的一种方法是通过继承,此时一个子类可以利用另一个基类的代码。
本教程将介绍 Python 中继承的一些主要方面,包括父类和子类如何工作、如何重写方法和属性、如何使用super()
函数,以及如何使用多重继承。
您应该在计算机或服务器上安装 Python 3 并设置编程环境。如果您还没有设置编程环境,可以参考安装和设置指南本地编程环境或对于一个服务器上的编程环境适合您的操作系统(Ubuntu、CentOS、Debian 等)
遗产是指一个类使用另一个类中构造的代码。如果我们从生物学的角度考虑遗传,我们可以认为孩子从父母那里继承了某些特征。也就是说,孩子可以继承父母的身高或眼睛颜色。孩子也可能与父母同姓。
类称为儿童班 or 子类继承方法和变量家长班 or 基类.
我们可以想到一个父类叫做Parent
具有类变量 for last_name
, height
, and eye_color
那个孩子班Child
将继承自Parent
.
因为Child
子类继承自Parent
基类,即Child
类可以重用代码Parent
,允许程序员使用更少的代码行并减少冗余。
父类或基类创建一个子类或子类可以基于的模式。父类允许我们通过继承来创建子类,而不必每次都重新编写相同的代码。任何类都可以成为父类,因此它们各自都是功能齐全的类,而不仅仅是模板。
假设我们有一个将军Bank_account
父类有Personal_account
and Business_account
儿童班。个人账户和企业账户之间的许多方法都是相似的,例如取款和存款的方法,因此这些可以属于Bank_account
. The Business_account
子类将具有特定于它的方法,可能包括收集业务记录和表单的方法,以及employee_identification_number
多变的。
同样,一个Animal
类可能有eating()
and sleeping()
方法,以及一个Snake
子类可能包含其自己的特定hissing()
and slithering()
方法。
让我们创建一个Fish
我们稍后将使用它的父类来构造鱼的类型作为其子类。除了特征之外,每条鱼都有名字和姓氏。
Info:要按照本教程中的示例代码进行操作,请通过运行以下命令在本地系统上打开 Python 交互式 shellpython3
命令。然后,您可以复制、粘贴或编辑示例,方法是将它们添加到>>>
prompt.
我们将创建一个名为的新文件fish.py
并从__init__()构造方法,我们将填充first_name
and last_name
每个类的变量Fish
对象或子类。
fish.py
class Fish:
def __init__(self, first_name, last_name="Fish"):
self.first_name = first_name
self.last_name = last_name
我们已经初始化了我们的last_name
带有字符串的变量"Fish"
因为我们知道大多数鱼都会以此作为姓氏。
我们还添加一些其他方法:
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.")
我们添加了方法swim()
and swim_backwards()
to the Fish
类,以便每个子类也能够使用这些方法。
因为我们要创造的大多数鱼都被认为是硬骨鱼(因为他们有一个由骨头制成的骨架)而不是软骨鱼(因为它们有一个由软骨制成的骨架),我们可以添加更多属性__init__()
method:
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.")
构建父类遵循与构建任何其他类相同的方法,除了我们正在考虑创建子类后将能够使用哪些方法。
子类或子类是从父类继承的类。这意味着每个子类将能够使用父类的方法和变量。
例如,一个Goldfish
子类Fish
类将能够利用swim()
中声明的方法Fish
无需声明。
我们可以将每个子类视为父类的一个类。也就是说,如果我们有一个名为Rhombus
和一个名为的父类Parallelogram
,我们可以说一个Rhombus
is a Parallelogram
,就像一个Goldfish
is a Fish
.
子类的第一行看起来与非子类略有不同,因为您必须将父类作为参数传递给子类:
The Trout
类是 的子级Fish
班级。我们知道这一点是因为包含了这个词Fish
在括号内。
对于子类,我们可以选择添加更多方法,覆盖现有的父方法,或者接受默认的父方法pass
关键字,在本例中我们将执行以下操作:
fish.py
...
class Trout(Fish):
pass
我们现在可以创建一个Trout
对象,而无需定义任何其他方法。
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()
我们创建了一个Trout
object terry
它利用了每个方法Fish
即使我们没有在类中定义这些方法Trout
儿童班。我们只需要传递值"Terry"
to the first_name
变量,因为所有其他变量都已初始化。
当我们运行该程序时,我们将收到以下输出:
Output
Terry Fish
bone
False
The fish is swimming.
The fish can swim backwards.
接下来,让我们创建另一个包含自己的方法的子类。我们将这个类称为Clownfish
,其特殊的方法将使其能够与海葵一起生活:
fish.py
...
class Clownfish(Fish):
def live_with_anemone(self):
print("The clownfish is coexisting with sea anemone.")
接下来,让我们创建一个Clownfish
对象看看它是如何工作的:
fish.py
...
casey = Clownfish("Casey")
print(casey.first_name + " " + casey.last_name)
casey.swim()
casey.live_with_anemone()
当我们运行该程序时,我们将收到以下输出:
Output
Casey Fish
The fish is swimming.
The clownfish is coexisting with sea anemone.
输出显示Clownfish
object casey
能够使用Fish
方法__init__()
and swim()
以及它的子类方法live_with_anemone()
.
如果我们尝试使用live_with_anemone()
方法在一个Trout
对象,我们会收到一个错误:
Output
terry.live_with_anemone()
AttributeError: 'Trout' object has no attribute 'live_with_anemone'
这是因为该方法live_with_anemone()
仅属于Clownfish
儿童班,而不是Fish
父类。
子类继承其所属父类的方法,因此每个子类都可以在程序中使用这些方法。
到目前为止,我们已经了解了子类Trout
那个利用了pass
关键字继承所有父类Fish
行为,和另一个子类Clownfish
它继承了所有父类的行为,并创建了自己特定于子类的独特方法。然而,有时我们会想要利用一些父类行为,但不是全部。当我们改变父类方法时override them.
在构造父类和子类时,记住程序设计非常重要,这样重写就不会产生不必要或多余的代码。
我们将创建一个Shark
的儿童班Fish
父类。因为我们创建了Fish
课程的想法是我们将主要创造硬骨鱼,我们将不得不做出调整Shark
相反,它是软骨鱼的一类。在程序设计方面,如果我们有不止一种非硬骨鱼,我们很可能希望为这两种鱼中的每一种创建单独的类。
与硬骨鱼不同,鲨鱼的骨骼是由软骨而不是骨头制成的。它们也有眼睑,无法向后游。然而,鲨鱼可以通过下沉来向后移动。
鉴于此,我们将重写__init__()
构造方法和swim_backwards()
方法。我们不需要修改swim()
方法,因为鲨鱼是会游泳的鱼。我们来回顾一下这个子类:
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.")
我们已经覆盖了初始化参数__init__()
方法,使得last_name
变量现在设置为等于字符串"Shark"
, the skeleton
变量现在设置为等于字符串"cartilage"
,以及eyelids
变量现在设置为布尔值True
。该类的每个实例也可以覆盖这些参数。
方法swim_backwards()
现在打印的字符串与中的字符串不同Fish
父类,因为鲨鱼不能像硬骨鱼那样向后游。
我们现在可以创建一个实例Shark
子类,它仍然会使用swim()
的方法Fish
父类:
fish.py
...
sammy = Shark("Sammy")
print(sammy.first_name + " " + sammy.last_name)
sammy.swim()
sammy.swim_backwards()
print(sammy.eyelids)
print(sammy.skeleton)
当我们运行此代码时,我们将收到以下输出:
Output
Sammy Shark
The fish is swimming.
The shark cannot swim backwards, but can sink backwards.
True
cartilage
The Shark
子类成功覆盖了__init__()
and swim_backwards()
的方法Fish
父类,同时也继承swim()
父类的方法。
当存在有限数量的比其他类更独特的子类时,重写父类方法可能会被证明是有用的。
随着super()
函数,您可以访问已在类对象中覆盖的继承方法。
当我们使用super()
函数时,我们将父方法调用到子方法中以使用它。例如,我们可能想用某些功能重写父方法的一个方面,但然后调用原始父方法的其余部分来完成该方法。
在给学生评分的程序中,我们可能想要一个儿童班级Weighted_grade
继承自Grade
父类。在幼儿班里Weighted_grade
,我们可能想要覆盖calculate_grade()
父类的方法,以便包含计算加权成绩的功能,但仍保留原始类的其余功能。通过调用super()
函数我们就能够实现这一点。
The super()
函数最常用的是__init__()
方法,因为这是您最有可能需要为子类添加一些唯一性,然后从父类完成初始化的地方。
为了看看这是如何工作的,让我们修改我们的Trout
儿童班。由于鳟鱼是典型的淡水鱼,让我们添加一个water
变量为__init__()
方法并将其设置为等于字符串"freshwater"
,但随后维护父类的其余变量和参数:
fish.py
...
class Trout(Fish):
def __init__(self, water = "freshwater"):
self.water = water
super().__init__(self)
...
我们已经覆盖了__init__()
方法中的Trout
子类,提供不同的实现__init__()
已由其父类定义Fish
。内__init__()
我们的方法Trout
我们已经显式调用了类__init__()
的方法Fish
class.
因为我们已经重写了该方法,所以我们不再需要传递first_name
作为参数Trout
,如果我们确实传递了一个参数,我们将重置freshwater
反而。因此我们将初始化first_name
通过调用我们的对象实例中的变量。
现在我们可以调用父类的初始化变量,也可以使用唯一的子变量。让我们在一个实例中使用它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()
Output
Terry Fish
False
freshwater
The fish is swimming.
输出显示该对象terry
of the Trout
子类能够利用子类特定的__init__()
多变的water
同时还能够调用Fish
parent __init__()
的变量first_name
, last_name
, and eyelids
.
内置Python函数super()
允许我们使用父类方法,即使在子类中重写这些方法的某些方面也是如此。
多重继承是指一个类可以从多个父类继承属性和方法。这可以让程序减少冗余,但也会引入一定程度的复杂性和歧义性,因此应该在整体程序设计时考虑清楚。
为了展示多重继承是如何工作的,让我们创建一个Coral_reef
子类比继承自Coral
类和一个Sea_anemone
班级。我们可以在每个方法中创建一个方法,然后使用pass
中的关键字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
The Coral
类有一个方法叫做community()
打印一行,并且Anemone
类有一个方法叫做protect_clownfish()
打印另一行。然后我们将这两个类都调用到继承中tuple。这意味着CoralReef
继承自两个父类。
现在让我们实例化一个CoralReef
object:
珊瑚礁.py
...
great_barrier = CoralReef()
great_barrier.community()
great_barrier.protect_clownfish()
物体great_barrier
被设置为CoralReef
对象,并且可以使用两个父类中的方法。当我们运行该程序时,我们将看到以下输出:
Output
Coral lives in a community.
The anemone is protecting the clownfish.
输出显示两个父类的方法在子类中得到了有效的使用。
多重继承允许我们在子类中使用多个父类的代码。如果在多个父类方法中定义了相同的方法,则子类将使用其元组列表中声明的第一个父类的方法。
尽管可以有效地使用多重继承,但应谨慎使用多重继承,以免我们的程序变得含糊不清且难以让其他程序员理解。
本教程介绍了构建父类和子类、重写子类中的父方法和属性,使用super()
函数,并允许子类继承多个父类。
面向对象编码中的继承可以遵守软件开发的 DRY(不要重复自己)原则,从而可以用更少的代码和重复完成更多的工作。继承还迫使程序员思考如何设计他们正在创建的程序,以确保代码有效且清晰。