一、继承
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题。
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又称之为基类或超类,新建的类称为派生类或子类。
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass
class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
pass
# 查看继承:
print( SubClass1.__bases__)
print(SubClass2.__bases__)
打印结果:
(<class '__main__.ParentClass1'>,)
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
二、继承属性的查找顺序
lass Foo:
def f1(self):
print('from Foo.f1')
def f2(self):
print('from Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('from Bar.f1')
print(Bar.__bases__) # 查看继承的父类
b = Bar()
b.f1() # 从b本身的名称空间查找
打印结果:from Bar.f1
b.f2()
打印结果:
from Foo.f2
from Bar.f1
由于b对象本身没有f2,开始从Bar类的名称空间查找f2,Bar类的名称空间也没有f2,则从其父类Foo名称空间查找f2程序运行到f2()下面的self.f1()这句时此时这句可改写为b.f1(),运行到这行时即从b对象本身名称空间查找f1,而不是运行Foo类名称空间中的f1
# 查看继承属性的顺序
print(Bar.mro()) # 以列表形式打印
打印结果:
[<class '__main__.Bar'>, <class '__main__.Foo'>, <class 'object'>]
三、经典类和新类
# 经典类:无继承objict的类,已经该类的子类
>>> class A:pass
...
>>> A.__bases__
()
>>> class B(A):pass
...
>>> B.__bases__
(<class __main__.A at 0x0000000002D19828>,)
# 新类:
>>> A.__bases__
(<type 'object'>,)
>>> class B(A):pass
1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
注意:在python中如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
四、继承的实现原理
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1、子类会先于父类被检查
2、多个父类会根据它们在列表中的顺序被检查
3、如果对下一个类存在两个合法的选择,选择第一个父类
在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先
1、深度优先
深度优先是按照如下顺序去查找属性的,直到找到该属性。即先安一个分支一直找到顶层的父类,然后在返回来找另外的分支
当类是经典类时,多继承情况下,在查找属性不存在时,会按照深度优先的方式查找下去
2、广度优先
广度优先是按照如下顺序去查找属性的,直到找到该属性。即先按照一个分支去查找,直到遇到顶层的父类后返回,查找下一个分支,最后查找顶层的父类
当类是经典类时,多继承情况下,在查找属性不存在时,会按照广度优先的方式查找下去
class A:
def text(self):
print('from A')
class B(A):
# def text(self):
# print('from B')
pass
class C(A):
# def text(self):
# print('from C')
pass
class D(B):
# def text(self):
# print('from D')
pass
class E(B):
# def text(self):
# print('from E')
pass
class F(C):
# def text(self):
# print('from F')
pass
class G(C):
# def text(self):
# print('from G')
pass
class H(D,E):
# def text(self):
# print('from H')
pass
class I(F,G):
# def text(self):
# print('from I')
pass
class J(H,I):
# def text(self):
# print('from J')
pass
a = J()
print(J.mro())
a.text()
输出结果:[<class '__main__.J'>, <class '__main__.H'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.I'>, <class '__main__.F'>, <class '__main__.G'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
from A
五、在子类中重用父类的方法和属性
在子类中重用父类的方法和属性是为了减少重复代码,重用共有两种方法。
1、指名道姓,即父类名.父类方法()
方法一:指名道姓法(不依赖继承)
class Hero:
# 定义每个英雄的名字,生命值,攻击力,防御
def __init__(self, name, life_vale, aggressivity, physical_defense, magic_defense):
self.name = name
self.life_vale = life_vale
self.aggressivity = aggressivity
self.physical_defense = physical_defense
self.magic_defense = magic_defense
# 定义一个生产英雄的类
class ADC(Hero):
camp = 'high aggressivity'
def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
Hero.__init__(self,name, life_vale, aggressivity, physical_defense, magic_defense) # 指明要调用父类的那些属性和方法
self.weapon = weapon
# 定义英雄的技能和技能的伤害值
def attack(self,enemy):
enemy.life_vale = enemy.life_vale - self.aggressivity * (1-enemy.physical_defense) * 2
class AP(Hero):
camp = 'high skill aggressivity'
def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
Hero.__init__(self,name, life_vale, aggressivity, physical_defense, magic_defense) # 指明要调用父类的那些属性和方法
self.weapon = weapon
def skill(self, enemy):
enemy.life_vale = enemy.life_vale - self.aggressivity * (1 - enemy.magic_defense)
adc = ADC('艾希',2000,300,0.3,0.4,'arrow')
ap = AP('维克托',2000,90,0.4,0.3,'truncheon')
print(adc.__dict__)
print(ap.__dict__)
输出结果:
{'name': '艾希', 'life_vale': 2000, 'aggressivity': 300, 'physical_defense': 0.3, 'magic_defense': 0.4, 'weapon': 'arrow'}
{'name': '维克托', 'life_vale': 2000, 'aggressivity': 90, 'physical_defense': 0.4, 'magic_defense': 0.3, 'weapon': 'truncheon'}
2、super()
# 定义一个生产英雄的类
class ADC(Hero):
camp = 'high aggressivity'
def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
super(ADC, self).__init__(name, life_vale, aggressivity, physical_defense, magic_defense)
# 此种方法依赖继承,这句其本质上是类的属性绑定
self.weapon = weapon
# 定义英雄的技能和技能的伤害值
def attack(self,enemy):
enemy.life_vale = enemy.life_vale - self.aggressivity * (1-enemy.physical_defense) * 2
class AP(Hero):
camp = 'high skill aggressivity'
def __init__(self,name, life_vale, aggressivity, physical_defense, magic_defense,weapon):
super().__init__(name, life_vale, aggressivity, physical_defense, magic_defense)
self.weapon = weapon
def skill(self, enemy):
enemy.life_vale = enemy.life_vale - self.aggressivity * (1 - enemy.magic_defense)
adc = ADC('艾希',2000,300,0.3,0.4,'arrow')
ap = AP('维克托',2000,90,0.4,0.3,'truncheon')
print(adc.__dict__)
print(ap.__dict__)
输出结果:
{'name': '艾希', 'life_vale': 2000, 'aggressivity': 300, 'physical_defense': 0.3, 'magic_defense': 0.4, 'weapon': 'arrow'}
{'name': '维克托', 'life_vale': 2000, 'aggressivity': 90, 'physical_defense': 0.4, 'magic_defense': 0.3, 'weapon': 'truncheon'}
3、 super()继承原理
class A:
def f1(self):
print('from A')
super().f3()
class B:
def f2(self):
print('from B')
class D:
def f3(self):
print('from D')
# super().f2()
class C(A,B,D):
pass
c = C()
print(C.mro())
c.f1()
输出结果:
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class 'object'>]
from A
from D
由于c中没有f1就按照C中的mro列表顺序到A中查找f1,A中有f1则执行f1的函数,首先打印出来 ’from A‘此时程序继续向下走开始执行super().f3(),由于super()具基于C的mro列表查找顺序继续向下查找的性质,开始到B中查找f3,B中没有f3则继续向下到D中查找f3,在D中找到f3,输出结果
六、组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
# 定义人类
class People:
school = 'luffycity'
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
# 定义老师类
class Teacher(People):
def __init__(self,name,sex,age,level,salary):
super().__init__(name,sex,age)
self.level = level
self.salary = salary
# 定义学生类
class Student(People):
def __init__(self,name,sex,age,class_time):
super().__init__(name,sex,age)
self.class_time = class_time
# 定义学生类
class Course:
def __init__(self,course_name,course_price,course_period):
self.course_name = course_name
self.course_price = course_price
self.course_period = course_period
def course_info(self):
info = '''
课程:%s
价格:%s
时长:%s
'''%(self.course_name,self.course_price,self.course_period)
print(info)
class Data:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
def data_info(self):
print('%s-%s-%s'%(self.year,self.month,self.day))
teacher1 = Teacher('刘淑','女',26,50,10000) # 实例化老师类对象
teacher2 = Teacher('夏天','男',28,70,20000)
student1 = Student('王战','男',30,'07:30:00')
student2 = Student('狗熊','男',30,'09:30:00') # 实例化学生类对象
python = Course('python',12000,'6 months') # 实例化课程类对象
linux = Course('linux',8000,'5 months')
data = Data(1993,5,16)
student1.course = python
student2.course1 = python
student2.course2 = linux
student2.course1.course_info() # 将课程对象组合到学生对象中:组合是两个对象的关系为 xx 有 xx
student1.datas = data
student1.datas.data_info()
1.继承的方式
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
2.组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好