文章目录
继承
继承是面向对象编程中的一个重要概念。通过继承,我们可以让一个类获取到其他类中的属性和方法,避免编写重复性的代码,并且符合开闭原则(OCP)。继承是使一个类扩展的常用方式。
定义一个类 Animal
我们先定义一个类 Animal,这个类中有两个方法 run() 和 sleep(),表示动物会跑和睡觉。
python
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
定义一个类 Dog
现在,我们想定义一个类 Dog,这个类除了能够跑和睡觉外,还能够汪汪叫。我们可以直接修改 Animal 类,在其中添加 bark() 方法,但这样会违反 OCP 原则。更好的方式是通过继承。
python
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
在上面的代码中,我们使用 class Dog(Animal):
来指定 Dog 类继承自 Animal 类。这样,Dog 类就能够直接获取到 Animal 类的属性和方法。我们还可以重写 Animal 类中的方法,如在 Dog 类中重新定义了 run() 方法。
创建对象并调用方法
现在我们可以创建 Dog 类的对象 d = Dog()
,并调用它的方法,比如 d.run()
、d.sleep()
和 d.bark()
。
python
d = Dog()
d.run() # 输出:狗跑~~~~
d.sleep() # 输出:动物睡觉~~~
d.bark() # 输出:汪汪汪~~~
类之间的关系
通过使用继承,我们可以建立类之间的层次关系。在创建类时,如果省略了父类,则默认父类为 object
。object
是所有类的父类,所以所有类都继承自 object
。
python
class Person(object):
pass
我们可以使用 issubclass()
函数检查一个类是否是另一个类的子类,如 issubclass(Animal, Dog)
返回 False
,而 issubclass(Animal, object)
返回 True
。
我们还可以使用 isinstance()
函数来检查一个对象是否是一个类的实例。如果这个类是这个对象的父类,也会返回 True
。所有的对象都是 object
的实例。
python
print(isinstance(d, Dog)) # 输出:True
print(isinstance(d, Animal)) # 输出:True
print(isinstance(d, object)) # 输出:True
print(isinstance(print, object)) # 输出:True
继承使得类之间的关系更加清晰,并且方便代码的复用和扩展。
多重继承
除了单一继承外,Python 还支持多重继承,即一个子类可以从多个父类中继承属性和方法。这为我们提供了更大的灵活性,使得代码的组织和复用更加方便。
定义一个类 Hashiqi
假设我们还有一个类 Hashiqi,表示一种特殊的狗,它除了具备狗的基本行为外,还有自己特有的方法 fan_sha(),表示傻傻的哈士奇。
python
class Hashiqi(Dog):
def fan_sha(self):
print('我是一只傻傻的哈士奇')
在上面的代码中,我们定义了一个 Hashiqi 类,它继承自 Dog 类。因此,Hashiqi 类不仅能够拥有 Animal 类和 Dog 类中的属性和方法,还具备自己特有的 fan_sha() 方法。
创建对象并调用方法
现在我们可以创建 Hashiqi 类的对象 h = Hashiqi()
,并调用它的方法,比如 h.run()
、h.sleep()
、h.bark()
和 h.fan_sha()
。
python
h = Hashiqi()
h.run() # 输出:狗跑~~~~
h.sleep() # 输出:动物睡觉~~~
h.bark() # 输出:汪汪汪~~~
h.fan_sha() # 输出:我是一只傻傻的哈士奇
通过多重继承,我们可以实现更灵活和具有复杂关系的类结构。在创建子类时,只需指定多个父类,并且子类可以直接获取到所有父类中的属性和方法,从而减少了代码的冗余。
继承是面向对象编程的重要特性之一,它能够提高代码的可读性、可维护性和可扩展性,并符合开闭原则。在设计类的时候,我们应该充分考虑继承关系,遵循良好的代码组织和设计原则。
当一个类继承了多个父类时,可能会遇到命名冲突的问题。比如,如果两个父类都有相同名称的方法或属性,那么在子类中调用这个名称时会出现歧义。为了解决这个问题,Python 提供了方法解析顺序(Method Resolution Order,简称MRO)。
方法解析顺序(MRO)
在 Python 中,每个类都有一个方法解析顺序,即它继承的父类被搜索的顺序。可以通过 类名.__mro__
或者 类名.mro()
来查看方法解析顺序。下面是一个例子:
python
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
print(D.__mro__)
print(D.mro())
运行上面的代码,会输出以下结果:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
从输出结果可以看出,方法解析顺序是 D -> B -> C -> A -> object。也就是说,在 D 类的实例上调用 method()
方法时,会按照该顺序依次查找,并执行第一个匹配到的方法。
super() 函数
super() 函数是用于调用父类方法的一种方式。可以使用 super().方法名()
的形式来调用父类的方法,而不需要显式地指定父类的名称。比如:
python
class A:
def method(self):
print("A")
class B(A):
def method(self):
super().method()
print("B")
b = B()
b.method()
输出结果为:
A
B
在上述代码中,B 类继承自 A 类,并在自己的 method() 方法中使用 super().method() 这一方式调用了父类 A 中的 method() 方法,并在其后打印了 "B"。
使用 super() 函数,可以保证在多重继承时按照方法解析顺序依次调用父类的方法,从而避免命名冲突和歧义。
当在多重继承中存在钻石继承(diamond inheritance)的情况时,为了避免方法重复调用和冗余代码,Python 使用 C3 线性化算法来确定方法解析顺序(Method Resolution Order,MRO)。
C3 线性化算法
C3 线性化算法通过合并多个父类的线性化顺序,生成一个满足以下条件的线性化列表:
- 子类永远在父类前面。
- 如果一个类在列表中的前面出现,那么它的所有父类也都在其前面。
- 如果多个父类都在一个类的后面,那么它们的顺序保持不变。
这样,在方法解析顺序中,每个类的方法只会被调用一次,避免了重复调用和冗余代码。
示例
以下是一个钻石继承的示例:
python
class A:
def method(self):
print("A")
class B(A):
pass
class C(A):
def method(self):
print("C")
class D(B, C):
pass
d = D()
d.method()
在上述代码中,类 D 继承了 B 和 C,而 B 和 C 都继承了 A。当我们创建 D 的实例并调用 method()
方法时,根据 C3 线性化算法得到的方法解析顺序是 D -> B -> C -> A,因此输出结果为 "C"。
super() 函数和钻石继承
在使用 super() 函数时,Python 会根据 MRO 确定的方法解析顺序来调用父类的方法。在钻石继承的情况下,super() 函数会按照 MRO 的顺序依次调用每个父类的方法,并保证每个方法只被调用一次。
python
class A:
def method(self):
print("A")
class B(A):
def method(self):
super().method()
print("B")
class C(A):
def method(self):
super().method()
print("C")
class D(B, C):
pass
d = D()
d.method()
运行上述代码,输出结果为:
A
C
B
在该示例中,类 D 的方法解析顺序是 D -> B -> C -> A。当调用 d.method()
方法时,super().method() 会依次调用 B、C 和 A 类的 method() 方法。通过 MRO,每个类的方法只会被调用一次,避免了重复调用和冗余代码。
经典类和新式类
在 Python 2.x 版本中,存在经典类和新式类的概念。经典类是指没有显式继承自 object 的类,而新式类则是显式继承自 object 的类。
在经典类中,Python 使用深度优先搜索的方法来解析方法调用顺序。这种方法存在一些问题,比如方法重复调用、无法实现多重继承时的方法解析顺序等。
在新式类中,Python 使用 C3 线性化算法来解析方法的调用顺序。这种方法可以保证方法只被调用一次,并且解决了多重继承时方法解析顺序不确定的问题。
为了更好地兼容新旧版本的 Python,在 Python 2.3 版本中引入了一个特殊语法,即在定义类时使用 class ClassName(object):
的形式,从而将所有类都定义为新式类。
在 Python 3.x 版本中,则已经默认将所有类定义为新式类,无需显式继承自 object。
总结
Python 中的多重继承给程序员提供了更灵活的设计选择,但也带来了一些挑战。为了避免命名冲突和歧义,在多重继承中需要正确地设置方法解析顺序(MRO),从而保证方法调用的正确性和效率。
Python3.x 已经默认将所有类定义为新式类,并使用 C3 线性化算法来解决多重继承的问题,不再需要特别注意 MRO 的设置。
在 Python 中,钻石继承(diamond inheritance)是指一个子类同时继承自两个有共同父类的类,形成了一个菱形的继承结构。这种继承结构会引发一些问题,例如方法重复调用和冗余代码。
为了解决钻石继承带来的问题,Python 使用 C3 线性化算法来确定方法的解析顺序(Method Resolution Order,MRO)。具体步骤如下:
- 按照类的声明顺序,生成一个拓扑排序的列表(DAG)。
- 在拓扑排序的列表中,检查每个节点的父类列表,并将其父类所在的位置移动到它自身的前面。这样,可以保证子类在父类之前。
- 对于多个父类同时出现在同一个节点之后的情况,需要按照它们在基类列表中的顺序保持不变。
通过使用 C3 线性化算法,Python 可以避免方法重复调用和冗余代码的问题。此外,Python 会在类的定义过程中自动计算 MRO,并将其存储在特殊属性 __mro__
中,供开发者查看。
需要注意的是,在实际使用中,当存在钻石继承的情况时,可以通过合理设计类的继承关系和使用 super()
函数来避免问题的出现。合理利用多态性、组合等技术也可以减轻继承带来的复杂性。
python精品专栏推荐
python基础知识(0基础入门)
【python基础知识】0.print()函数
【python基础知识】1.数据类型、数据应用、数据转换
【python基础知识】2.if条件判断与条件嵌套
【python基础知识】3.input()函数
【python基础知识】4.列表和字典
【python基础知识】5.for循环和while循环
【python基础知识】6.布尔值和四种语句(break、continue、pass、else)
【python基础知识】7.实操-用Python实现"文字PK"小游戏(一)
【python基础知识】7.实操-用Python实现"文字PK"小游戏(二)
【python基础知识】8.编程思维:如何解决问题-思维篇
【python基础知识】9.函数的定义和调用
【python基础知识】10.用函数编写程序 - 实操篇
【python基础知识】10.用Python实现石头剪刀布小游戏-函数实操篇
【python基础知识】11.如何debug -常见报错原因及排查思路 - 思维篇
【python基础知识】12.类与对象(一)
【python基础知识】12.类与对象(二)
【python基础知识】13.类与对象(三)
【python基础知识】13.类与对象(四)
【python基础知识】14.图书管理系统的搭建(类与对象实操)
【python基础知识】15.编码基础知识
【python基础知识】16.文件读写基础及操作
【python基础知识】16."古诗默写题"的python实现(文件读写和编码-实操篇)
【python基础知识】17.模块的概念以及如何引入
【python基础知识】18.实操-使用python自动群发邮件
【python基础知识】19.产品思维以及流程图的使用 - 思维篇
【python基础知识】20."午饭吃什么"的python实现(产品思维-实操篇)
【python基础知识】21.高效偷懒的正确打开方式-毕业篇
【python文件处理】CSV文件的读取、处理、写入
【python文件处理】Excel自动处理(使用 openpyxl)
【python文件处理】-excel格式处理
python爬虫知识
【python爬虫】1.爬虫基础知识
【python爬虫】2.网页基础知识
【python爬虫】3.爬虫初体验(BeautifulSoup解析)
【python爬虫】4.爬虫实操(菜品爬取)
【python爬虫】5.爬虫实操(歌词爬取)
【python爬虫】6.爬虫实操(带参数请求数据)
【python爬虫】7.爬到的数据存到哪里?
【python爬虫】8.温故而知新
【python爬虫】9.带着小饼干登录(cookies)
【python爬虫】10.指挥浏览器自动工作(selenium)
【python爬虫】11.让爬虫按时向你汇报
【python爬虫】12.建立你的爬虫大军
【python爬虫】13.吃什么不会胖(爬虫实操练习)
【python爬虫】14.Scrapy框架讲解
【python爬虫】15.Scrapy框架实战(热门职位爬取)
【python爬虫】16.爬虫知识点总结复习