python-继承与重用

Posted cook

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python-继承与重用相关的知识,希望对你有一定的参考价值。

什么是继承?

继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。子类会“”遗传”父类的属性,从而解决代码重用问题(比如练习7中Garen与Riven类有很多冗余的代码)

单继承与多继承

class ParentClass_one: #定义父类
pass

class ParentClass_two: #定义父类
pass

class SubClass_one(ParentClass_one): #单继承,基类是ParentClass1,派生类是SubClass
pass

class SubClass_two(ParentClass_one,ParentClass_two): #python支持多继承,用逗号分隔开多个继承的类
pass

经典类与新式类

  • 只有在python2中才分新式类和经典类,python3中统一都是新式类
  • 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
  • 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
  • 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

继承与抽象

  • 继承描述的是子类与父类之间的关系,是一种什么是什么的关系。要找出这种关系,必须先抽象再继承,抽象即抽取类似或者说比较像的部分。抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
  • 继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

继承与重用性

在开发程序的过程中,如果我们定义了一个类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 Garen(Hero):
pass

class Riven(Hero):
pass

g=Garen(\'草丛伦\',100,300)
r=Riven(\'锐雯雯\',57,200)

print(g.life_value)
r1.attack(g)
print(g.life_value)

\'\'\'
运行结果
243
\'\'\'
ps:像g.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类。

派生

子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准。

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)

r=Riven(\'锐雯雯\',57,200,\'比基尼\')
r.fly()
print(r.skin)

\'\'\'
运行结果
锐雯雯 is flying
比基尼

\'\'\'

组合与重用性

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

class Equip: #武器装备类
def fire(self):
print(\'release Fire skill\')

class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
camp=\'Noxus\'
def __init__(self,nickname):
self.nickname=nickname
self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性

r=Riven(\'锐雯雯\')
print(r.equip.fire()) #可以使用组合的类产生的对象所持有的方法

\'\'\'
release Fire skill
\'\'\'

组合与继承

  • 继承的方式:通过继承建立了派生类与基类之间的关系,它是一种\'是\'的关系,比如白马是马,人是动物。当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
  • 组合的方式:用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...

ps:当类之间有显著不同,并且较小的类是较大的类所需要的组件时,使用组合比较好

class People:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex

class Course:
def __init__(self,name,period,price):
self.name=name
self.period=period
self.price=price
def tell_info(self):
print(\'<%s %s %s>\' %(self.name,self.period,self.price))

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=[]


jack=Teacher(\'jack\',18,\'male\',\'computer teacher\')
s=Student(\'tom\',18,\'female\')

python=Course(\'python\',\'3mons\',3000.0)
linux=Course(\'python\',\'3mons\',3000.0)

#为老师jack和学生s1添加课程
jack.course.append(python)
jack.course.append(linux)
s.course.append(python)

#为老师jack添加学生s
jack.students.append(s)


#使用
for obj in jack.course:
obj.tell_info()

继承实现原理

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D),如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性,如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先


 

 

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中才分新式类与经典类

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列表并遵循如下三条准则:

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类

子类调用父类的方法
方法一:指名道姓,即父类名.父类方法()

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()、

方法二:super()

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) 就相当于实例本身 在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()

class Mobike(Vehicle):#摩拜单车
pass

line13=Subway(\'中国地铁\',\'180m/s\',\'1000人/箱\',\'\',13)
line13.run()

 

以上是关于python-继承与重用的主要内容,如果未能解决你的问题,请参考以下文章

java 代码片段

面向对象之继承与派生

面向对象之继承与派生

python继承小demo

继承与派生

python3 面向对象类继承组合派生接口子类重用父类方法