python 3.2 对象的的特性 -- 继承 组合
目录
1、继承总结 is -a 的问题
什么是继承 ? 继承是一种新建类的方式,新建的类
为何用继承? 子类会遗传父类的属性,所以继承解决类于类之间的代码冗余
继承是一种同一种维度下事务的遗传,有着相似的功能点
继承讲解了 is -a的关系 ,一个交通工具的范围里有汽车 ,飞机 ,轮船 ,那么每一个子类可以继承父类的功能和数据,解决的对象冗余
继承中新式类和经典类,涉及的多继承和单继承,以及继承延申的菱形问题,又包含了广度优先和深度优先,最后python统一一个标准mixin,可以小范围的额外的使用多继承。
mixin的规范 不论对继承中的父类和子类来说,都是某一维度内的东西。
1 继承的优点 - 解决类属性的冗余
如何继承
print(Sub.__bases__) # 查看继承的父类的名称
一 解决 类中的属性冗余
class OldboyPeople():
school = 'oldboy'
class Stuend(OldboyPeople):
def __init__(self,name,age,smale):
self.name = name
self.age =age
self.smale = smale
def choose(self):
print('%s 正在选课'%(self.name))
class Terchar(OldboyPeople):
def __init__(self,name,age,male,salar,level):
self.name = name
self.age = age
self.male = male
self.salar = salar
self.level = level
def score(self,stu,num):
stu.num = num
stu= Stuend('zhangsan',18,'man')
ter = Terchar('laoshi',90,'man',9000,'10')
print(stu.school)
子类派生中重用父类方法的方式一 指名道姓
在子类派生的新方法中重用父类的功能方法一
类属性的 继承于 父类
父类有的函数f1 ,子类如果也有的话,用子类的
使用不依赖继承关系的 重用父类的方法
class OldboyPeople():
school = 'oldboy'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def f1(self):
print('111')
class Stuend():
def __init__(self,name,age,gender):
OldboyPeople.__init__(self,name,age,gender)
# 在子类派生的新方法中 重用了父类的功能,在这个基础之上,扩展了自己的新的功能
def choose(self):
print('%s 正在选课'%(self.name))
class Terchar():
def __init__(self,name,age,gender,salar,level):
OldboyPeople.__init__(self,name,age,gender)
# 在子类派生的新方法中 重用了父类的功能,在这个基础之上,扩展了自己的新的功能
# 在调用 类函数的方法,没有自动传对象名,所以要加上空对象,调用类的函数
self.salar = salar
self.level = level
def score(self):
OldboyPeople.f1(self)
print('2222222')
stu= Stuend('zhangsan',18,'man')
ter = Terchar('laoshi',90,'man',9000,'10')
ter.score()
# 指名道姓的调用某一个类的功能,不依赖 继承关系
2继承中属性查找的优先级
继承不要搞太多层,一般是3层
太多的属性,如何快速的的查找?
继承背景下的属性查找的路径 就是 对象自己,类,父类
class foo():
def f1(self):
print('foo,1111')
def f2(self):
print('foo 2222')
self.f1()
class bar(foo):
def f1(self):
print('bar 1111')
stu = bar()
stu.f2()
检验继承背景下的属性查找优先级
需求 调用f2 ,要打印 foo 1111 ,实现
class foo():
def __f1(self):
print('foo,1111')
def f2(self):
print('foo 2222')
self.__f1()
class bar(foo):
def f1(self):
print('bar 1111')
stu = bar()
stu.f2()
使用 类 隐藏函数的功能使函数名发生变形,让执行顺序发生改变。
3类继承的实现原理 - 新式类 C3算法
类的继承的实现原理
class Aaa():
pass
print(Aaa.__bases__)
(<class 'object'>,) 在Python3 中如果一个类没有继承任何类的话,解释器会自动继承一个 object类。
那么在Python3 中,只要使继承了object 那么该子类的子子孙孙类都称之为新式类
那么在Python2 中,没有继承了object 那么该子类的子子孙孙类都称之为经典类
如果兼容Python2 的程序也要 继承 object
继承的菱形问题,一个子类继承的多条件分支最总汇聚到一个非object类,在菱形继承下
新式类 与经典类 关于属性查找方式不同
新式类: 广度有先
经典类: 深度有先
python3 新式类的广度查找
class A():
def f1(self):
print('A....')
pass
class B(A):
# def f1(self):
# print('B....')
pass
class C(A):
def f1(self):
print('C....')
pass
class D(B,C):
pass
obj = D()
obj.f1()
在菱形继承的情况下,解决为什么要广度优先的排序,由C3算法实现
查看排序的列表的方法使
print(D.mro())
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
继承反应的问题 就是 is -a 的意思
为什么要让子类继承父类 ,说明 子类满足父类的某一种属性,而多继承违背了这种关系
为什么python的设计者,设计了这种多继承的关系 ?
为了让子类最大限度的使用父类的代码,
为了让一个体 有多个不同的维度,更加的广泛,
如何防止多继承的滥用?
Python给出了标准使用 mixin
mixin 标准 白话就是在
在相同维度得情况下,(父类),为同维度下的个体(子类),增加了其他的功能。
例如 交通工具 这个父类 , 汽车, 飞机,轮船,这3个子类。
fly_mixin(可以飞的功能) 这个类,对于子类来说可以多继承使用。
那么这个功能必须使单一 ,也不依赖子类的实现。有没有继承,只是少了这个功能,不影响子类的实现
class B():
# def f1(self):
# print('B....')
pass
class C_mixin():
def f1(self):
print('C....')
pass
class D(C_mixin,B): #在使用多继承的时候,表达归属关系的类放在最右边。其他的类都是功能类,混入的功能,
pass
obj = D()
obj.f1()
4子类派生的新方法中重用父类功能的方式二 < super 属性的查找和 继承中的属性查找 相似 >
使用 super关键字, 调用 super(当前的类名,self对象名)语法格式,会返回一个特殊的对象,
super(Stuend,self). 属性会参照当前类的mro列表中,从父类开始,从左往右找。(严谨一点就是属性发起者的类的mro列表的父类开始找)
所以 在子类派生的新方法中重用父类功能的,不需要传对象名了
严格按照依赖继承的,会自动传参
class OldboyPeople():
school = 'oldboy'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def f1(self):
print('111')
class Student(OldboyPeople):
def __init__(self,name,age,gender,class_num):
super(Student,self).__init__(name,age,gender) # 重用父类的功能不需要再传入对象名
# OldboyPeople.__init__(self,name,age,gender)
self.class_mun =class_num
def choose(self):
print('%s 正在选课'%(self.name))
class Terchar(OldboyPeople):
def __init__(self,name,age,gender,salar,level):
# OldboyPeople.__init__(self,name,age,gender)
# super(Terchar, self).__init__(name,age,gender) # 在Python3 super关键字不需要在加入 当前类名和self
super().__init__(name,age,gender)
self.salar = salar
self.level = level
def score(self):
# OldboyPeople.f1(self)
print('2222222')
super().f1()
stu= Student('zhangsan',18,'man','五年级')
# print(stu.__dict__) # 打印了 当前类的功能和重用父类的功能
# {'name': 'zhangsan', 'age': 18, 'gender': 'man', 'class_mun': '五年级'}
ter = Terchar('lisi',18,'male',9000,10)
ter.score()
加强认识(严谨一点就是属性发起者的类的mro列表的父类开始找)
问题,会不会报错?
class A():
def test(self):
# print('....')
super(A, self).test()
class B():
def test(self):
print('....')
class C(A,B):
# def test(self):
# print('....')
pass
obj = C()
obj.test()
# 不会报错!!
print(C.mro())
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
定义 从上面的例子中,打印c的mro列表中看出,c的父类时A,A的父类是B 所以不会报错
但是在代码中 A和B 之间使没有继承关系的,但是还是以mro列表为准
升级 mro列表的认识
class A():
def test(self):
# print('....')
super(A, self).aaa()
class B():
def aaa(self):
print('B ....')
class C(A,B):
def aaa(self):
print('C ....')
pass
obj = C()
obj.test()
# 不会报错!!
print(C.mro())
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
!! 属性查找的关系
继承: 对象没有的,去当前类去找,当前类没有去父类找,按照mro列表,从左到右,如果父类调用了属性发起者的函数,以mro表优先,不会掉头去调用当前类的函数
super 属性查找, 只是跳过了 对象本身,和父类本身, 如果属性发起者的父类 super调用了函数,还是要以当前类的父类中去找,类本身有的话,也不会去执行。
总结
super的调用会返回一个对象,这个对象会参照 属性发起者的mro列表,去找 .之后属性,不要从肉眼层面去看,要看c3算法给的结果
2、组合的总结 has -a的问题
那么不同类,不同维度的东西如何组合, 比如 交通工具 --- 》 汽车 ----》 要烧什么油 ,,油和交通工具就不是一个维度的。 比如 人类 ---->老师 ----- > 课程 ,那么课程和人类也不是一个维度的。 汽车要烧汽油,老师要教课,如何在代码中把两个不同的类,进行组合使用哪。 在学习库的导入和 函数嵌套,就是用到了名称空间的概念,一个函数的属性,关联了另外一个名称空间 那么现在不同类的调用也是使用的是名称空间的概念 涉及一个实例A,调用实例B,,首先设置A的名称空间的一个属性等于实例B,那么,a.属性.B的功能和数据 才能点出来。 from .a import ls ls.xxx 从导入的形式上和组合几乎一致。
需求 学生、老师、课程 ,要求 查看老师和学生的课程。
分析 老师和学生 ,可以有一个父类
课程 单一的,使用组合 连接老师和学生的实例
class School():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
school = 'old'
class Student(School):
def __init__(self, name, age, gender):
super(Student, self).__init__(name, age, gender)
class Tercher(School):
def __init__(self, name, age, gender, salary, level):
super(Tercher, self).__init__(name, age, gender)
self.salary = salary
self.level = level
class Course():
def __init__(self, name, price, period):
self.name = name
self.price = price
self.period = period
def tell(self):
print('%s:%s:%s' % (self.name, self.price, self.period))
stu1 = Student('zhangsan', 19, 'male')
ter1 = Tercher('lisi',90,'man',9000,10)
python = Course('python',3000,'3Month')
linux = Course('linux',2000,'1Month')
stu1.courses = python
ter1.courses = [python,linux] # 这个列表里的元素就不能使字符串了,应该是对象
# stu1.courses.tell()
for courses_obj in ter1.courses:
courses_obj.tell() # 对象使用功能
浙公网安备 33010602011771号