Python 继承
- 类继承简化代码:Python允许从现有类创建新类(子类),继承其属性 和方法,促进代码重用。
- 方法覆盖增加灵活性:子类可以覆盖超类方法,实现定制化功能,同时保持结构的一致性。
- super()方法提升可用性:使用super()方法,子类能够访问并扩展超类的功能,增强了代码的可维护性和扩展性。
像其他面向对象编程语言一样,Python也支持类继承的概念。
继承允许我们从现有的类创建一个新类。
创建的新类被称为子类(子类或派生类),而子类派生自的现有类被称为超类(父类或基类)。
Python继承语法
以下是Python中继承的语法,
# 定义一个超类
class super_class:
# 属性和方法定义
# 继承
class sub_class(super_class):
# super_class的属性和方法
# sub_class的属性和方法
这里,我们从super_class
类继承了sub_class
类。
示例1:Python继承
class Animal:
# 父类的属性和方法
name = ""
def eat(self):
print("我会吃")
# 继承自Animal
class Dog(Animal):
# 子类中的新方法
def display(self):
# 使用self访问超类的name属性
print("我的名字是", self.name)
# 创建一个子类的对象
labrador = Dog()
# 访问超类的属性和方法
labrador.name = "Rohu"
labrador.eat()
# 调用子类方法
labrador.display()
输出
我会吃
我的名字是Rohu
在上面的示例中,我们从超类Animal派生了一个子类Dog。注意这些语句,
labrador.name = "Rohu"
labrador.eat()
这里,我们使用labrador(Dog的对象)来访问Animal类的name和eat()
。这是因为子类继承了超类的所有属性和方法。
此外,我们在Dog类的方法内部使用self
访问了name属性。
is-a关系
在Python中,继承是一个is-a关系。也就是说,我们只有在两个类之间存在is-a关系时才使用继承。例如,
- Car是一个Vehicle
- Apple是一个Fruit
- Cat是一个Animal
这里,Car可以从Vehicle继承,Apple可以从Fruit继承,等等。
示例2:Python中的继承
让我们看看Python中继承的另一个例子,
多边形是一个具有3个或更多边的封闭图形。假设我们有一个名为Polygon
的类,定义如下,
class Polygon:
def __init__(self, no_of_sides):
self.n = no_of_sides
self.sides = [0 for i in range(no_of_sides)]
def inputSides(self):
self.sides = [float(input("输入边"+str(i+1)+"的长度:")) for i in range(self.n)]
def dispSides(self):
for i in range(self.n):
print("边",i+1,"的长度是",self.sides[i])
这个类有数据属性来存储边的数量n
和作为一个列表称为sides
的每边的大小。
inputSides()
方法接收每边的长度dispSides()
方法显示这些边的长度
三角形是具有3边的多边形。所以,我们可以创建一个从Polygon
继承的名为Triangle
的类。这使得Polygon
类的所有属性都可用于Triangle类。
我们不需要再次定义它们 (代码复用性)。 Triangle
可以定义如下。
class Triangle(Polygon):
def __init__(self):
Polygon.__init__(self,3)
def findArea(self):
a, b, c = self.sides
# 计算半周长
s = (a + b + c) / 2
# 使用海伦公式计算三角形的面积
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('三角形的面积是 %0.2f' %area)
然而,Triangle
类有一个新方法findArea()
来找出并打印三角形的面积。
现在让我们看看上面的完整工作代码,包括创建一个对象,
class Polygon:
# 初始化边的数量
def __init__(self, no_of_sides):
self.n = no_of_sides
self.sides = [0 for i in range(no_of_sides)]
def inputSides(self):
self.sides = [float(input("输入边"+str(i+1)+"的长度:")) for i in range(self.n)]
# 显示多边形每边的长度的方法
def dispSides(self):
for i in range(self.n):
print("边",i+1,"的长度是",self.sides[i])
class Triangle(Polygon):
# 通过调用Polygon类的__init__方法,初始化三角形的边数为3
def __init__(self):
Polygon.__init__(self,3)
def findArea(self):
a, b, c = self.sides
# 计算半周长
s = (a + b + c) / 2
# 使用海伦公式计算三角形的面积
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('三角形的面积是 %0.2f' %area)
# 创建Triangle类的实例
t = Triangle()
# 提示用户输入三角形的边
t.inputSides()
# 显示三角形的边
t.dispSides()
# 计算并打印三角形的面积
t.findArea()
输出
输入边1的长度:3
输入边2的长度:5
输入边3的长度:4
边1的长度是3.0
边2的长度是5.0
边3的长度是4.0
三角形的面积是6.00
在这里,我们可以看到,即使我们没有为Triangle
类单独定义inputSides()
或dispSides()
方法,我们仍然可以使用它们。
如果在类本身中找不到属性,则搜索将继续到基类。如果基类本 身来自其他类,这将递归重复。
Python继承中的方法覆盖
在前面的示例中,我们看到子类的对象可以访问超类的方法。
然而,如果超类和子类中都存在相同的方法呢?
在这种情况下,子类中的方法会覆盖超类中的方法。这种概念被称为Python中的方法覆盖。
示例:方法覆盖
class Animal:
# 父类的属性和方法
name = ""
def eat(self):
print("我会吃")
# 继承自Animal
class Dog(Animal):
# 覆盖eat()方法
def eat(self):
print("我喜欢吃骨头")
# 创建一个子类的对象
labrador = Dog()
# 调用labrador对象的eat()方法
labrador.eat()
输出
我喜欢吃骨头
在上面的示例中,Dog类和Animal类中都存在相同的方法eat()
。
现在,当我们使用Dog子类的对象调用eat()
方法时,将调用Dog类的方法。
这是因为Dog子类中的eat()
方法覆盖了Animal超类中的同名方法。
Python继承中的super()方法
之前我们看到,子类中的相同方法会覆盖超类中的方法。
然而,如果我们需要从子类中访问超类的方法,我们使用super()
方法。例如,
class Animal:
name = ""
def eat(self):
print("我会吃")
# 继承自Animal
class Dog(Animal):
# 覆盖eat()方法
def eat(self):
# 使用super()调用超类的eat()方法
super().eat()
print("我喜 欢吃骨头")
# 创建一个子类的对象
labrador = Dog()
labrador.eat()
输出
我会吃
我喜欢吃骨头
在上面的示例中,Dog子类的eat()
方法覆盖了Animal超类的同名方法。
在Dog类内部,我们使用了
# 调用超类的方法
super().eat()
来从Dog子类调用Animal超类的eat()
方法。
因此,当我们使用labrador对象调用eat()
方法时
# 调用eat()方法
labrador.eat()
覆盖的方法和超类版本的eat()
方法都被执行了。
继承的用途
- 由于子类可以继承父类的所有功能,这允许代码复用。
- 一旦一个功能开发出来,你就可以简单地继承它。无需重新发明轮子。这使得代码更干净,更易于维护。
- 由于你还可以在子类中添加自己的功能,你可以只继承有用的功能,并定义其他所需的特性。