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 something

def 喝(self):
# do something

def 拉(self):
# do something

def 撒(self):
# do something

class 狗:

def 汪汪叫(self):
print '喵喵叫'

def 吃(self):
# do something

def 喝(self):
# do something

def 拉(self):
# do something

def 撒(self):
# do something

==========================第二部分
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:

  动物:吃、喝、拉、撒

   猫:喵喵叫(猫继承动物的功能)

   狗:汪汪叫(狗继承动物的功能)

伪代码如下:
class 动物:

def 吃(self):
# do something

def 喝(self):
# do something

def 拉(self):
# do something

def 撒(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

上一篇:python程序设计——面向对象程序设计:继承


下一篇:.net学习笔记----有序集合SortedList、SortedList、SortedDictionary