Python3 面向对象-类的继承与派生
Posted yujiaershao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python3 面向对象-类的继承与派生相关的知识,希望对你有一定的参考价值。
1、什么是继承?
继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类可称为基类或超类,新建的类称为派生类和或子类。
子类会遗传父类的属性,从而解决代码重用问题。
python中类的继承分为:单继承和多继承。
class ParentClass1:
pass
class ParentClass2:
pass
class SubClass1(ParentClass1): # 单继承,基类是 ParentClass1,派生类是SubClass1
pass
class SubClass2(ParentClass1, ParentClass2): # 多继承,用逗号分隔开多个继承的类。
pass
查看继承:
print(SubClass1.__bases__) # (<class ‘__main__.ParentClass1‘>,)
print(SubClass2.__bases__) # (<class ‘__main__.ParentClass1‘>, <class ‘__main__.ParentClass2‘>)
经典类与新式类
1.只有在python2中才分新式类和经典类,python3中统一都是新式类 2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类 3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类 3.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
print(ParentClass1.__bases__) # (<class ‘object‘>,)
print(ParentClass2.__bases__) # (<class ‘object‘>,)
2、继承与抽象
继承描述的是子类与父类之间的关系,是一种什么是什么的关系,必须先继承再抽象。
抽象即抽取比较像的部分。
抽象分成两个层次:
1)路人甲和路人乙这俩人比较像的部分抽取成类。
2)将人,猪,狗这三类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧。通过抽象可以得到类。
3、继承与重用性
==========================第一部分
例如猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:
#猫和狗有大量相同的内容
class 猫:def 喵喵叫(self):
print ‘喵喵叫‘def 吃(self):
# do somethingdef 喝(self):
# do somethingdef 拉(self):
# do somethingdef 撒(self):
# do somethingclass 狗:
def 汪汪叫(self):
print ‘喵喵叫‘def 吃(self):
# do somethingdef 喝(self):
# do somethingdef 拉(self):
# do somethingdef 撒(self):
# do something
==========================第二部分
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
伪代码如下:
class 动物:def 吃(self):
# do somethingdef 喝(self):
# do somethingdef 拉(self):
# do somethingdef 撒(self):
# do something# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):def 喵喵叫(self):
print ‘喵喵叫‘
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 狗(动物):def 汪汪叫(self):
print ‘喵喵叫‘
==========================第三部分
#继承的代码
class Animal:
def eat(self):
print(‘%s 吃‘ % self.name)
def drink(self):
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def move_forward(self):
print(‘%s move forward‘ %self.nickname)
def move_backward(self):
print(‘%s move backward‘ %self.nickname)
def move_left(self):
print(‘%s move forward‘ %self.nickname)
def move_right(self):
print(‘%s move forward‘ %self.nickname)
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class ChengYaojin(Hero):
pass
class HouYi(Hero):
pass
g1=ChengYaojin(‘程咬金‘,100,300)
r1=HouYi(‘后裔‘,57,200)
print(g1.life_value) # 300
r1.attack(g1)
print(g1.life_value) # 243
用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,节省了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.
class Foo:
def f1(self):
print(‘Foo.f1‘)
def f2(self):
print(‘Foo.f2‘)
self.f1()
class Bar(Foo):
def f1(self):
print(‘Foo.f3‘)
b=Bar()
b.f2()
# Foo.f2 Foo.f3
# 先从自己的方法里面找
4、派生
子类也可以添加自己的新属性,或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性与父类重名,那么调用该属性,以自己的为准。
class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def move_forward(self):
print(‘%s move forward‘ %self.nickname)
def move_backward(self):
print(‘%s move backward‘ %self.nickname)
def move_left(self):
print(‘%s move forward‘ %self.nickname)
def move_right(self):
print(‘%s move forward‘ %self.nickname)
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class ChengYaojin(Hero):
pass
class HouYi(Hero):
camp=‘Noxus‘
def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
print(‘from HouYi‘)
enemy.life_value += self.aggressivity
def fly(self): #在自己这里定义新的
print(‘%s is flying‘ %self.nickname)
g1=ChengYaojin(‘程咬金‘,100,300)
r1=HouYi(‘后裔‘,57,200)
print(g1.life_value)
r1.attack(g1) # 父类是减生命值,后裔的类中有attack函数,调用自己的attack函数,(300 + 57)
print(g1.life_value)
print(r1.life_value)
g1.attack(r1) # 父类是减生命值,程咬金的类没有attack函数,调用父类的attack函数,(200 - 100)
print(r1.life_value)
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为之传值。
class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def move_forward(self):
print(‘%s move forward‘ %self.nickname)
def move_backward(self):
print(‘%s move backward‘ %self.nickname)
def move_left(self):
print(‘%s move forward‘ %self.nickname)
def move_right(self):
print(‘%s move forward‘ %self.nickname)
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class ChengYaojin(Hero):
pass
class HouYi(Hero):
camp=‘Noxus‘
def __init__(self,nickname,aggressivity,life_value):
self.nickname = nickname
self.aggressivity = aggressivity
self.life_value = life_value
def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
print(‘from HouYi‘)
enemy.life_value += self.aggressivity
def fly(self): #在自己这里定义新的
print(‘%s is flying‘ %self.nickname)
H1 = HouYi(‘xiaohong‘, 50, 400)
print(H1.__dict__)
#################改写:class Hero:
def __init__(self,nickname,aggressivity,life_value):
self.nickname=nickname
self.aggressivity=aggressivity
self.life_value=life_value
def move_forward(self):
print(‘%s move forward‘ %self.nickname)
def move_backward(self):
print(‘%s move backward‘ %self.nickname)
def move_left(self):
print(‘%s move forward‘ %self.nickname)
def move_right(self):
print(‘%s move forward‘ %self.nickname)
def attack(self,enemy):
enemy.life_value-=self.aggressivity
class ChengYaojin(Hero):
pass
class HouYi(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 HouYi‘)
def fly(self): #在自己这里定义新的
print(‘%s is flying‘ %self.nickname)
H1 = HouYi(‘后裔‘, 50, 400, ‘yellow‘)
C1 = ChengYaojin(‘程咬金‘, 30, 500)
print(H1.skin)
print(C1.life_value)
H1.attack(C1)
print(C1.life_value)
5、组合与重用性
软件重用的重要方式除了继承之外还有另外一个方式:组合
软件组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合。
class Equip:
def fire(self):
print(‘release Fire skill‘)
class HouYi:
camp=‘蓝方‘
def __init__(self, nickname):
self.nickname = nickname
self.equip = Equip()
R1 = HouYi(‘后裔‘)
R1.equip.fire() # release Fire skill
组合与继承都是有效的利用已有的类资源的重要方式,但二者的概念和使用场景皆不同。
1、继承的方式
通过继承建立了派生类与基类直接的关系,它是一种‘是’的关系,比如黑猫是猫,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人,
2、组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如老师有生日,老师教mysql,老师有学生等。
class People:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Course:
def __init__(self, name, price, period):
self.name = name
self.price = price
self.period = period
def show_info(self):
print(‘<%s %s %s>‘ % (self.name, self.price, self.period))
class Teacher(People):
def __init__(self, name, age, sex, job_title):
People.__init__(self, name, age, sex)
self.job_title = job_title
self.course=[]
self.students=[]
class Student(People):
def __init__(self, name, age, sex):
People.__init__(self, name, age, sex)
self.course = []
T1 = Teacher(‘Mr Yu‘, 36, ‘男‘, ‘首席头牌‘)
S1 = Student(‘小明‘, 14, ‘男‘)
python=Course(‘python‘,‘2500‘, 12)
linux=Course(‘linux‘,‘2000‘,10)
#为老师T1和学生S1添加课程
T1.course.append(python)
T1.course.append(linux)
S1.course.append(linux)
#为老师T1i添加学生S1
T1.students.append(S1)
for obj in T1.course:
obj.show_info()
当类之间有显著不同,并且较小的类是较大类所需要的组件时,用组合比较好。
6、抽象类
1)什么是抽象类
与java一样,python也有抽象类的概念,但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
2)为什么要有抽象类
如果说类是从一堆对象中抽取出来的,那么抽象类就是从一堆类中抽取相同的内容而来的。内容包括数据属性和函数属性。
从设计角度来看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从现实角度来看,抽象类与普通类的不同之处在于,抽象类只能有抽象的方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的。
3)在python中实现抽象类。
#_*_coding:utf-8_*_
#一切皆文件
import abc # 利用abc模块实现抽象类
class All_File(metaclass=abc.ABCMeta):
all_type = ‘file‘
@abc.abstractmethod # 定义抽象方法,无需实现功能
def read(self):
‘子类必须定义读功能‘
pass
@abc.abstractmethod #定义抽象方法,无需实现功能
def write(self):
‘子类必须定义写功能‘
pass
# class Txt(All_File): #子类继承抽象类,但是必须定义read和write方法
# pass # 子类没有定义抽象类方法,所以报错
# t1 = Txt() #TypeError: Can‘t instantiate abstract class Txt with abstract methods read, write
class Txt(All_File): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print(‘文本数据的读取方法‘)
def write(self):
print(‘文本数据的写入方法‘)
t1 = Txt()
t1.read() #文本数据的读取方法
t1.write() #文本数据的写入方法
4)抽象类与接口
抽象类本质上还是类,指的是一组类的相似性,包括数据属性,函数属性。而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计。
7、继承实现的原理
继承原理(python如何实现的继承)
python到底是如何实现继承的,对于你定义的每一个类,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‘>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
# 继承顺序
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中才分新式类与经典类
子类调用父类的方法
方法一:指名道姓,即父类名.父类方法()
# coding = gbk
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(‘GO,GO,GO‘)
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 %s线欢迎您!‘ % (self.name, self.line))Vehicle.run(self)Line4 = Subway(‘北京地铁‘, ‘200km/s‘, ‘1200人‘, ‘电‘, ‘13号‘)
Line4.run()输出:
北京地铁 13号线欢迎您!
GO,GO,GO
方法二:super()
# coding = gbk
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(‘GO,GO,GO‘)
class Subway(Vehicle):
def __init__(self, name, speed, load, power, line):
#Vehicle.__init__(self ,name, speed, load, power) #原方式
super().__init__(name, speed, load, power) #python3中,super() 等同于 super(Subway, self)
self.line = line
def run(self):
print(‘%s %s线欢迎您!‘ % (self.name, self.line))
#Vehicle.run(self)
super().run()
Line4 = Subway(‘北京地铁‘, ‘200km/s‘, ‘1200人‘, ‘电‘, ‘13号‘)Line4.run()输出:
北京地铁 13号线欢迎您!
GO,GO,GO
以上是关于Python3 面向对象-类的继承与派生的主要内容,如果未能解决你的问题,请参考以下文章