面向对象-继承与派生
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象-继承与派生相关的知识,希望对你有一定的参考价值。
1.继承1.1继承概念
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题。
继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类可以成为基类或超类,新建的类成为派生类或子类
1.2python中类的继承分类:单继承和多继承
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
pass
1.3查看继承
"__base__只查看从左到右继承的第一个父类
__bases__查看所有继承的父类"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
pass
print(SubClass1.__base__)
print(SubClass2.__base__)
print(SubClass1.__bases__)
print(SubClass2.__bases__)
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
<class ‘__main__.ParentClass1‘>
<class ‘__main__.ParentClass1‘>
(<class ‘__main__.ParentClass1‘>,)
(<class ‘__main__.ParentClass1‘>, <class ‘__main__.ParentClass2‘>)
Process finished with exit code 0
1.4经典类与新式类
1.只有在Python2中才有新式类和经典类,Python3中全部都是新式类
2.在Python2中,如果没有显式的继承object类,那该类和其子类都是经典类
3.在Python2中,如果显式的继承object类,那该类和其子类都是新式类
4.在Python3中,无论是否继承object,都默认继承object,即Python3中所有类都是新式类
5.在Python3中,如果没有指定基类,会默认继承object类,object是所有Python类的基类
print(ParentClass1.__bases__)
print(ParentClass2.__bases__)
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
(<class ‘object‘>,)
(<class ‘object‘>,)
Process finished with exit code 0
2.抽象与继承
抽象:抽取类似的部分,主要用于划分类别
继承:基于抽象的结果,通过编程语言去实现它,所以需要先对事物或逻辑进行抽象,之后通过继承的方式表达出抽象的结构
3.继承与重用
在开发中,如果类A与类B有很多相同的代码,就可以通过继承来实现,提高代码的重用性
可以让B继承A,B会‘遗传’A的所有属性(包含数据属性和函数属性)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class Garen(Hero):
pass
class Riven(Hero):
pass
g1=Garen(‘草丛伦‘,100,300)
r1=Riven(‘锐雯雯‘,57,200)
print(g1.life_value) #结果:300
r1.attack(g1)
print(g1.life_value) #结果:243
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
300
243
Process finished with exit code 0
"用已经有的类新建一个新的类,这样就可以重用已经有的软件中的部分代码,大大节省编程工作量,这就是软件重用,不仅可以重用自己的类,还可以继承别人写的类,也可继承标准库。"
4.属性查找与self深入理解
不管是类中的数据属性还是函数属性,都是先在自己的类中查找,没有,再到父类中查找
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class Foo:
def f1(self):
print(‘Foo.f1‘)
def f2(self):
print(‘Foo.f2‘)
self.f1()
class Bar(Foo):
def f1(self):
print(‘Bar.f1‘)
b=Bar()
#f2()在Bar类中没有,就去父类Foo中找,父类中的f2又调用self.f1,同样先去自己的类中找,找到了,就返回。
b.f2()
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
Foo.f2
Bar.f1
Process finished with exit code 0
5.派生
子类也可以添加自己的新属性或重新定义父类中含有的数据属性和函数属性,子类中新定义的属性对父类中的属性是没有影响的,但一旦重新定义的自己的属性与父类中的属性重名,调用新的属性就以自己的为准了
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class Garen(Hero):
pass
class Riven(Hero):
camp=‘Noxus‘
def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
print(‘from riven‘)
def fly(self): #在自己这里定义新的
print(‘%s is flying‘ %self.nickname)
g1=Garen(‘草丛伦‘,100,300)
r1=Riven(‘锐雯雯‘,57,200)
r1.attack(g1)
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
from riven
Process finished with exit code 0
在子类中,新建了与父类中重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中的同名的那个函数,此时调用父类中同名函数的方法:类名.func(),此时就与调用普通函数相同了,因此self也要为其传值
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class Garen(Hero):
pass
class Riven(Hero):
camp=‘Noxus‘
def __init__(self,nickname,aggressivity,life_value,skin):
Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能
self.skin=skin #新属性
def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
Hero.attack(self,enemy) #调用功能
print(‘from riven‘)
def fly(self): #在自己这里定义新的
print(‘%s is flying‘ %self.nickname)
r1=Riven(‘锐雯雯‘,57,200,‘比基尼‘)
r1.fly()
print(r1.skin)
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
锐雯雯 is flying
比基尼
Process finished with exit code 0
6.深度优先与广度优先
6.1MRO列表介绍
上面我们一直在说,对于数据属性和函数属性的查找,
首先找对象自身的定义空间,即对象.__dict__的结果,
如果没有,去自己的类中找,
自己类中没有,再去父类中找。
但是当继承比较复杂,有多个父类,父类还有父类,这种情况下是怎样的一个查找顺序呢?
python有一个方法解析顺序表(MRO),这个MRO列表就是一个属性的查找顺序列表,找到了相关的属性就停止查找,否则就一直按照这个顺序找到最后,找不到,就报错了
>>> F.mro() #等同于F.__mro__
[<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>,
<class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>]
MRO列表遵循三条准则:
1.子类优先于父类被检查
2.多个父类会根据他们在列表中的顺序去检查
3.如果下一个类中存在两个合法的选择,选择第一个父类
6.2新式类与经典类
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class A(object):
def test(self):
print(‘from A‘)
class B(A):
def test(self):
print(‘from B‘)
class C(A):
def test(self):
print(‘from C‘)
class D(B):
def test(self):
print(‘from D‘)
class E(C):
def test(self):
print(‘from E‘)
class F(D,E):
# def test(self):
# print(‘from F‘)
pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
from D
(<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>)
Process finished with exit code 0
"当C不继承A了,新式类与经典类的顺序就相同了"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class A(object):
def test(self):
print(‘from A‘)
class B(A):
def test(self):
print(‘from B‘)
class C:
def test(self):
print(‘from C‘)
class D(B):
def test(self):
print(‘from D‘)
class E(C):
def test(self):
print(‘from E‘)
class F(D,E):
# def test(self):
# print(‘from F‘)
pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
from D
(<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.A‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘object‘>)
Process finished with exit code 0
7.子类中调用父类的方法
7.1方式一:指名道姓,即父类名.方法名
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class Vehicle: # 定义交通工具类
Country = ‘China‘
def __init__(self, name, speed, load, power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print(‘开动啦...‘)
class Subway(Vehicle): # 地铁
def __init__(self, name, speed, load, power, line):
Vehicle.__init__(self, name, speed, load, power)# 指名道姓方式,调用父类中同名的方法
self.line = line
def run(self):
print(‘地铁%s号线欢迎您‘ % self.line)
Vehicle.run(self)# 指名道姓方式,调用父类中同名的方法
line13 = Subway(‘中国地铁‘, ‘180m/s‘, ‘1000人/箱‘, ‘电‘, 13)
line13.run()
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
地铁13号线欢迎您
开动啦...
Process finished with exit code 0
7.2super()方式
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
class Vehicle: # 定义交通工具类
Country = ‘China‘
def __init__(self, name, speed, load, power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print(‘开动啦...‘)
class Subway(Vehicle): # 地铁
def __init__(self, name, speed, load, power, line):
# super(Subway,self) # Python2的写法 在python3中super()等同于super(Subway,self)
super().__init__(name, speed, load, power)
self.line = line
def run(self):
print(‘地铁%s号线欢迎您‘ % self.line)
#super(Subway, self).run()# Python2的写法
super().run() #Python3中,可以不在super()中写类名和self
class Mobike(Vehicle): # 摩拜单车
pass
line13 = Subway(‘中国地铁‘, ‘180m/s‘, ‘1000人/箱‘, ‘电‘, 13)
line13.run()
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
地铁13号线欢迎您
开动啦...
Process finished with exit code 0
7.3两种方式的区别
"方式一与属性查找的mro顺序没有关系,直接指定了调用哪个类中的方法
方式二是会按照mro列表中的顺序,向后查找相应的属性"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
# "A没有继承B,但是A内super会基于C.mro()继续往后找"
class A:
def test(self):
super().test()
class B:
def test(self):
print(‘from B‘)
class C(A,B):
pass
c=C()
c.test() #打印结果:from B
print(C.mro())
E:\PythonProject\python-test\venvP3\Scripts\python.exe E:/PythonProject/python-test/BasicGrammer/test.py
from B
[<class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘__main__.B‘>, <class ‘object‘>]
Process finished with exit code 0
以上是关于面向对象-继承与派生的主要内容,如果未能解决你的问题,请参考以下文章