面向对象(二)--继承与派生、组合
一、什么是继承
继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。
二、继承的特点
1.继承描述的是事物的遗传关系,子类可以重用父类的属性,需要注意的是:继承是类与类之间的关系!!好处就是可以减少类之间的代码冗余问题
2.在Python中支持一个子类继承多个父类
3.Python类分为两种:新式类、经典类
(1)新式类: 但凡继承了object的类Foo,以及该类的子类...都是新式类
在python3中一个类即便是没有显式地继承任何类,默认就会继承object,即python3中所有的类都是新式类
(2)经典类:没有继承object的类,以及该类的子类...都是经典类
在python2中才区分新式类与经典类,在python2中一个类如果没有显式地继承任何类,也不会继承object
三、单继承和多继承
class Parent1(): a = 3 class Parent2(): a = 3 class Sub1(Parent1): # 单继承,父类是Parent1,子类是Sub1 a = 2 pass class Sub2(Parent1,Parent2): # 多继承,父类是Parent1,Parent2,子类是Sub1 a = 2 pass
四、查看继承
利用__bases__方法可以查看子类的父类,只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 class Parent1(): pass class Sub1(Parent1): pass print(Sub1.__bases__)
五、继承的实现原理
C3算法与方法解析顺序(MRO)列表
属性查找的原则:
- 子类先于父类被查找
- 有多个父类时,会根据它们在列表中的顺序被查找
- 如果对下一个类存在两个合法的选择,则选择第一个父类
mro列表只适用于新式类,经典类不存在mro列表
只有在python2中才有经典类与新式类的区分,python3中全部是新式类
六、子类重用父类中的方法(派生)
1、派生:在子类中定义自己的属性,如果与父类的属性重名,那以自己的为准
2、在子类派生出的新方法中重用父类功能的方式一:指名道姓地访问父类中的函数
Foo.__init__(self,name,age) 不能自动传值,有几个参数就传几个
(1)该方式与继承无关
(2)没有自动传值的效果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class People(): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class Student(People): def __init__(self, name, age, sex, score=0): # 方式一 调用父类中的__init__函数 People.__init__(self, name, age, sex) self.score = score def choose(self): print('%s choosing course' % self) class Teacher(People): def score(self, stu, score): stu.score = score stu = Student('张三', 12, 'male') print(stu.__dict__) # __dict__可以查看对象的名称空间中的名字
3、在子类派生出的新方法中重用父类功能的方式二:super(),只能在子类中用
super().__init__(name,age) 自动传值
(1)在python2中:super(自己的类名,自己的对象)
在python3:super()
调用super()会得到一个特殊的对象,该特殊的对象是专门用来引用父类中的属性的,!!!完全参照mro列表!!!
(2)super()注意点:
a. 该方式与继承严格依赖于继承的mro列表,类名.mro() ===> 不管有没有继承关系,super()都是根据mro往后查找
b. 访问是绑定方法,有自动传值的效果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class People(): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class Student(People): def __init__(self, name, age, sex, score=0): super().__init__(name,age,sex) self.score = score def choose(self): print('%s choosing course' % self) class Teacher(People): def score(self, stu, score): stu.score = score stu = Student('张三', 12, 'male') print(stu.__dict__) # 查看对象的名称空间
七、继承背景下的属性查找
1、单继承背景下的属性查找
单继承背景下属性查找顺序:对象的名称空间------>对象的类的名称空间------->父类的名称空间
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Foo: x=333 pass class Bar(Foo): # x=222 pass obj=Bar() # obj.x=111 print(obj.x) # 333
2、多继承背景下的属性查找
多继承背景下属性查找的顺序:对象的名称空间------>对象的类的名称空间-------->按照从左往右的顺序一个一个的分支找下去
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 查找顺序是 obj-->A-->B-->E-->C-->F-->I-->D-->H #第四层 class I: # x='I' pass #第三层 class E: # x='E' pass class F(I): # x='F' pass class H: x='H' # 第二层 class B(E): # x='B' pass class C(F): # x='C' pass class D(H): # x='D' pass #第一层 class A(B,C,D): # x='A' pass obj=A() # obj.x=111 print(obj.x)
八、继承顺序
1、在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)
2、如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性
如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先
九、组合
1、什么是组合
组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象
class Foo(): pass class Bar(): pass obj=Bar() obj.attrib=Foo()
2、组合的作用
通过为某一个对象添加属性(属性值是另外一个类的对象)的方式,可以间接地将两个类关联/整合/组合到一起,从而减少类与类之间代码冗余
3、组合的使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class OldboyPeople: school = 'oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class OldboyStudent(OldboyPeople): def __init__(self, name, age, sex, score=0): super().__init__(name, age, sex) self.score = score self.courses = [] def choose_course(self): print('%s choosing course' % self.name) def tell_all_course(self): print(('学生[%s]的课程如下' % self.name).center(60, '=')) for course in self.courses: course.tell_course() print('=' * 80) class OldboyTeacher(OldboyPeople): def __init__(self, name, age, sex, level): super().__init__(name, age, sex) self.level = level self.courses = [] def score(self, stu, num): stu.score = num def tell_all_course(self): print(('老师[%s]教授的课程如下' % self.name).center(70, '-')) for course in self.courses: course.tell_course() print('-' * 80) class Course: def __init__(self, c_name, c_price, c_period): self.c_name = c_name self.c_price = c_price self.c_period = c_period def tell_course(self): print('<课程名:%s 价格:%s 时间:%s>' % (self.c_name, self.c_price, self.c_period)) python = Course('python全栈开发', 10000, 5) linux = Course('linux架构', 12000, 5) stu = OldboyStudent('zs', 18, 'male') stu.courses.append(python) stu.courses.append(linux) stu.tell_all_course() teach = OldboyTeacher('egon', 18, 'male', 10) teach.courses.append(python) teach.tell_all_course()
软件重用的重要方式除了继承之外的另一种方式就是组合。
组合和继承都是有效的利用已有类的资源的重要方式,但使用场景和概念却都不同:
1、继承的方式:
通过继承建立了派生类与基类之间的关系,是一种“是”的关系。
2、组合的方式:
用组合的方式建立了类与组合的类之间的关系,是一种“有”的关系。
#当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合会更好。