注意:本文引用自专业人工智能社区Venus AI
更多AI知识请参考原站 ([www.aideeplearning.cn])
Python中的继承是面向对象编程(OOP)的一个基本特性,它允许一个类(称为子类)继承另一个类(称为父类或基类)的属性和方法。这里是关于Python继承的一些关键点:
基本概念
- 父类(基类):父类是被继承的类,它提供了子类可以继承的属性和方法。
- 子类(派生类):子类是从一个或多个父类继承属性和方法的类。
继承的优势
- 代码重用:继承允许子类重用父类的代码,这减少了代码的重复编写。
- 封装性:子类可以添加自己独特的属性和方法,同时继承父类的功能。
- 多态性:子类可以覆盖或修改父类的行为,这是面向对象编程中多态性的一个体现。
继承类型
- 单继承:子类只继承一个父类。这是最简单的继承形式。
- 多继承:子类可以继承多个父类。Python 支持多继承,这使得子类可以同时从多个基类继承属性和方法。
方法重写
子类可以重写继承自父类的方法。这意味着子类可以提供父类方法的一个特定于子类的新实现。
super() 函数
super()
函数在继承中发挥重要作用,它用于调用父类的方法。它特别在多继承场景中非常有用,可以正确地处理方法调用,遵循方法解析顺序(MRO)。
方法解析顺序(MRO)
MRO 是一个规则集,它决定了 Python 在多继承场景下如何搜索继承的方法。Python 使用一种称为 C3 线性化的算法来确定这个顺序,确保每个基类仅被访问一次,并且保持基类之间的适当顺序。
使用继承
在设计程序时,继承应谨慎使用。不恰当的使用可能导致代码难以理解和维护。一般建议,只有在子类确实是父类的一种特殊形式时,才使用继承。
继承是Python面向对象编程的核心特性之一,提供了强大的代码组织和重用机制。正确使用继承可以使代码更加清晰、灵活和可维护。
代码详细示例
1.继承的概念
在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承的概念就要简单得多了,即描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承自狗。代码如下:
#lei.py
# 定义一个父类,如下:
class Cat( ): def __init__(self, name, color ): self.name = name self.color = color def run(self): print("%s--在跑" %self.name)
# 子类在继承的时候,在定义类时,小括号()中为父类的名字,继承Cat类如下:
class TianyuanCat(Cat): def setNewName(self, newName): self.name = newName print(self.name)def eat(self): print("%s--在吃"%self.name)
Xuanmao = TianyuanCat("玄猫", "黑色")
print('Xuanmao的名字为:%s' %Xuanmao.name)
print('Xuanmao的颜色为:%s' %Xuanmao.color)
Xuanmao.eat()
Xuanmao.setNewName('小黑')
Xuanmao的名字为:玄猫 Xuanmao的颜色为:黑色 玄猫--在吃 小黑虽然子类TianyuanCat没有定义初始化方法和run方法,但是父类(Cat)有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建TianyuanCat的对象,就默认执行了那个继承过来的init()方法了。 此外,还需要注意的是: (1)类的私有的属性,不能通过对象直接访问,但是可以通过方法访问。 (2)私有的方法不能通过对象直接访问。 (3)私有的属性、方法不会被子类继承,也不能被访问。 (4)一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用。 代码如下:
#lei.py
class Animal(): def __init__(self, name='动物', color='白色'): self.__name = name self.color = color def __test(self): print(self.__name) print(self.color) def test(self): print(self.__name) print(self.color)
class Dog(Animal): def dogTest1(self): #print(self.__name) #不能访问到父类的私有属性 print(self.color) def dogTest2(self): #self.__test() #不能访问父类中的私有方法 self.test()
A = Animal()
#print(A.__name) #程序出现异常,不能访问私有属性
print(A.color)
#A.__test() #程序出现异常,不能访问私有方法
A.test()
print("------分割线-----")
D = Dog(name = "小花狗", color = "黄色")
D.dogTest1()
D.dogTest2()
白色 动物 白色 ------分割线----- 黄色 小花狗 黄色
2.多继承
所谓多继承,即子类有多个父类,并且具有它们的特征,如下图所示。 Python中的多继承代码如下:
#lei.py
# 定义一个父类
class A: def printA(self): print('----A----')
# 定义一个父类
class B: def printB(self): print('----B----')
# 定义一个子类,继承自A、B
class C(A,B): def printC(self): print('----C----')
obj_C = C()
obj_C.printA()
obj_C.printB()
obj_C.printC()
----A---- ----B---- ----C----
注意一点,如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用先继承的父类方法。代码如下:
#lei.py
class base(object): def test(self): print('----base test----')
class A(base): def test(self): print('----A test----')
# 定义一个父类
class B(base): def test(self): print('----B test----')
# 定义一个子类,继承自A、B
class C(A,B): pass
obj_C = C()
obj_C.test()
#输出:----B test----
----A test----
3. 调用与重写父类方法
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法,代码如下:
#lei.py
class Cat: def sayHello(self): print("miaomiao~~")
class Xuanmao(Cat): def sayHello(self): print("miao!~~!!!")
xiaohei = Xuanmao()
xiaohei.sayHello()
#输出:miao!~~!!!
miao!~~!!!
在类的继承里面super()方法是很常用的,它解决了子类调用父类方法的一些问题,例如,当父类多次被调用时只执行一次,优化了执行逻辑,下面我们来详细看一下。
当存在继承关系的时候,有时候需要在子类中调用父类的方法,此时最简单的方法是把对象调用转换成类调用,需要注意的是这时self参数需要显式传递,代码如下:
#lei.py
class Cat1: def sayHello(self): print("miaomiao~~")
class Xuanmao(Cat): def sayHello(self): # 调用父类方法事,直接使用父类名称进行调用 Cat1.sayHello(self)
xiaohei = Xuanmao()
xiaohei.sayHello()
#输出:miaomiao~~
miaomiao~~
当使用继承时,一个子类会继承父类的属性和方法。但如果直接在子类中通过父类的名称来调用父类的构造函数或方法,就会面临一些限制和问题:
父类名称修改:如果更改了父类的名称,那么所有子类中的相关代码也必须跟着更改。这在大型项目中,尤其是子类众多时,会导致维护成本显著增加。
多继承的复杂性:Python支持多继承,即一个子类可以有多个父类。在多继承的情况下,如果子类需要调用所有父类的方法,那么使用传统的方式就需要明确地重复每个父类的名称,这增加了代码的复杂性和冗余。
为了解决这些问题,Python提供了super()函数。如下示例所示
#lei.py
class Cat2321: def sayHello(self): print("miaomiao~~")
class Xuanmao(Cat): def sayHello(self): # 使用super()代替父类类名,即便类名改变,这里的super也不需要修改。 super().sayHello()
xiaohei = Xuanmao()
xiaohei.sayHello()
#输出:miaomiao~~
miaomiao~~
使用super()可以带来以下优势:
简化代码:super()允许你不必显式地引用父类,这使得代码更加简洁、易于理解和维护。
自动解析正确的方法:在多继承的情况下,super()知道应该调用哪个父类的方法,遵循所谓的方法解析顺序(MRO)。这减少了多继承时潜在的复杂性和错误。
动态继承:super()的动态特性意味着你不需要硬编码父类的名称,使得代码更加灵活。如果父类发生变化,子类的代码不需要修改。