Python面向对象之继承
一、继承
继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性。python支持多继承。
在Python中,新建的类可以继承一个或多个父类
继承描述的是类和类之间的关系,例如:a继承了b,a就能直接使用b已经存在的方法和属性。
代码例子:
# class Parent1(object): # x=1111 # # class Parent2(object): # pass # # class Sub1(Parent1): # 单继承 # pass # # class Sub2(Parent1,Parent2): # 多继承,继承了两个类 # pass # print(Sub1.__bases__) # print(Sub2.__bases__)
#注意:::::::
# 1: 在python2中有经典类与新式类之分
# 新式类:继承了object类的子类,以及该子类的子类子子类。。。
# 经典:没有继承object类的子类,以及该子类的子类子子类。。。
# 2:在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类
继承功能代码例子
class Base: ser="一个基类" def show_info(self): print(self.ser) def make_money(self): print("冲冲冲") # 指定父类位Base class Subclass(Base): pass obj=Subclass() # 即使类中什么都没有也可以使用父集中已经有的内容 obj.make_money() #结果:冲冲冲
print(obj.ser) #结果:一个基类
二、抽象与继承
抽象:就是不具体、不清晰的。抽象即抽取类似或者说比较像的部分。就是将几个类的共同部分抽取出来
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
例如:一个学生类与老师类,这两个类里面有相同的部分。将这一部分代码可以写成一个基类,然后就可以少写冗余代码。学生类和老师类继承这个基类
# 示范2:基于继承解决类与类之间的冗余问题 class People: school = 'ddd' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class Student(People): def choose_course(self): print('学生%s 正在选课' % self.name) # stu_obj = Student('aaa', 18, 'female') # print(stu_obj.__dict__) # print(stu_obj.school) # stu_obj.choose_course() class Teacher(People): # 老师的空对象,'aaa',18,'male',3000,10 def __init__(self, name, age, sex, salary, level): # 指名道姓地跟父类People去要__init__ People.__init__(self,name,age, sex) self.salary = salary self.level = level def score(self): print('老师 %s 正在给学生打分' % self.name) tea_obj=Teacher('aaa',18,'male',3000,10) # print(tea_obj.__dict__) # print(tea_obj.school) #tea_obj.score()
三、派生
若一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类;通常子类都会添加一些新的代码,不可能和父类完全一致,即通常都是派生类,所以派生类指的就是子类
class Person: def say_hi(self): print("hello") class Student(Person): def say_hi(self): print("hello world!") stu = Student() stu.say_hi() #hello world! #此时子类中出现了同一个方法,不同内容。就是派生
四、覆盖
覆盖也称之为重写:overrides 当子类出现了与父类名称完全一致的属性或方法根据查找顺序,优先使用子类中的属性。如上面代码实例
五、属性查找顺序
对象自己的-->>所在的类中->>找父类->>父类的父类->>Object(对象)
六、子类访问父类里面的内容
调用super()方法
方式:
方式1: super(当前类名称,self).你要调的父类的属性或方法
方式2: super().你要调的父类的属性或方法 #最常用的方式
方式3: 类名称.你要调的父类的属性或方法(self)
代码演示
class Parent: text = "abc" def say_something(self): print("anything") class Sub(Parent): def show_info(self): # print(super(Sub,self).text) # super(Sub,self).say_something() # 访问方式2 py3的新语法 最常用的方式 print(super().text) #abc,访问父类text数据属性 super().say_something() #anything,访问父类方法 #方式3 直接指定类名调用 # print(Parent.text) # Parent.say_something(self) sub = Sub() sub.show_info()
注意:当你要继承一个出现有的类,并覆盖了父类的__init__的方法时,必须在初始化方法的第一行调用父类的初始化,并传入父类所需的参数。
class MyList(list): def __init__(self,element_type): super().__init__() # 调用父类的初始化方法 来完成基本的初始化, self.element_type = element_type #自己在父类里增加了一个参数element_type一开始定义就设置限定条件 def append(self, object): """ :param object: 是要存储的元素 :return: 没有 """ if type(object) == self.element_type: #我们需要在这里访问父类的append函数来完成真正的存储操作 super(MyList,self).append(object) #采用方式一,调用父类的append方法 else: print("sorry sir, you element type not is %s" % self.element_type) # 创建是指定要存储的元素类型 m = MyList(int)
m.append(1) print(m[0]) m.append("33333")
使用super()函数时,Python会在MRO列表上继续搜索下一个类。如果每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次
#A没有继承B,但是A内super会基于C.mro()继续往后找 class A: def test(self): super().test() class B: def test(self): print('我是B') class C(A,B): pass c=C() c.test() #打印结果:我是B print(C.mro()) #[, , , ],此时就是mro列表,按照列表顺序一直找
七、组合
在一个类中以另外一个类的对象做为数据属性称为类的组合。
使用继承的条件: 分析两个类的关系,到底是不是:什么是什么的关系
使用组合的条件: 如果两个类之间 没有太大的关系,完全不属于同类
八、菱形继承
当出现了菱形继承时,新式类,先深度,当遇到了共同父类时就广度 ,新式类,就是深度优先。Python3里全是新式类
代码演示
class E(object): def __init__(self, name): self.name = name print('E') class P(E): def __init__(self, name, price): E.__init__(self, name) self.price = price print('P') class C(E): def __init__(self, name, config): E.__init__(self, name) self.config = config print('C') class H(P, C): def __init__(self, name, price, config): P.__init__(self, name, price) C.__init__(self, name, config) print('H') h = H('hua', 100, 'i7') #结果 ''' E P E C H '''
此时E的初始化被执行了两次,通过super可以解决这个问题
class E(object): def __init__(self, name): self.name = name print('E') class P(E): def __init__(self, price,*args): super(P, self).__init__(*args) self.price = price print('P') class C(E): def __init__(self, config,*args): super(C, self).__init__(*args) self.config = config print('C') class H(P, C): def __init__(self, name, price, config): super(H, self).__init__(name, price, config) print('H') h = H('hua', 100, 'i7') #结果 ''' E C P H '''
总结:先深度再广度,先下到上,再左到右
题:
class Init(object): def __init__(self, v): print("init") self.val = v class Add2(Init): def __init__(self, val): print("Add2") super(Add2, self).__init__(val) print(self.val) self.val += 2 class Mult(Init): def __init__(self, val): print("Mult") super(Mult, self).__init__(val) self.val *= 8 class HaHa(Init): def __init__(self, val): print("哈哈") super(HaHa, self).__init__(val) self.val /= 8 class Pro(Add2, Mult, HaHa): # pass class Incr(Pro): def __init__(self, val): super(Incr, self).__init__(val) self.val += 1 # Incr Pro Add2 Mult HaHa Init p = Incr(5) print(p.val) ''' Add2 Mult 哈哈 init 5.0 8.0 ''' # c = Add2(2) # print(c.val) ''' Add2 init 2 4 '''
class G: # 在python2中,未继承object的类及其子类,都是经典类 # def test(self): # print('from G') pass class E(G): # def test(self): # print('from E') pass class F(G): def test(self): print('from F') class B(E): # def test(self): # print('from B') pass class C(F): def test(self): print('from C') class D(G): def test(self): print('from D') class A(B,C,D): # def test(self): # print('from A') pass # 新式类 # print(A.mro()) # A->B->E->C->F->D->G->object # 经典类:A->B->E->G->C->F->D obj = A() obj.test() #
九、mixins机制
Mixins机制指的是子类混合 (mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系。
注意:
1、python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
2、其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
3、子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。
代码实例:
class Vehicle: pass class FlyableMixin: def fly(self): pass class CivilAircraft(FlyableMixin,Vehicle): # 民航飞机 pass class Helicopter(FlyableMixin,Vehicle): # 直升飞机 pass class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了 pass
继承关系分析:
class Displayer: def display(self, message): print(message) class LoggerMixin: def log(self, message, filename='logfile.txt'): with open(filename, 'a') as fh: fh.write(message) def display(self, message): super().display(message) self.log(message) class MySubClass(LoggerMixin, Displayer): def log(self, message): super().log(message, filename='subclasslog.txt') obj = MySubClass() obj.display("This string will be shown and logged in subclasslog.txt") # 属性查找的发起者是obj,所以会参照类MySubClass的MRO来检索属性 #[<class '__main__.MySubClass'>, <class '__main__.LoggerMixin'>, <class '__main__.Displayer'>, <class 'object'>] # 1、首先会去对象obj的类MySubClass找方法display,没有则去类LoggerMixin中找,找到开始执行代码 # 2、执行LoggerMixin的第一行代码:执行super().display(message),参照MySubClass.mro(),super会去下一个类即类Displayer中找,找到display,开始执行代码,打印消息"This string will be shown and logged in subclasslog.txt" # 3、执行LoggerMixin的第二行代码:self.log(message),self是对象obj,即obj.log(message),属性查找的发起者为obj,所以会按照其类MySubClass.mro(),即MySubClass->LoggerMixin->Displayer->object的顺序查找,在MySubClass中找到方法log,开始执行super().log(message, filename='subclasslog.txt'),super会按照MySubClass.mro()查找下一个类,在类LoggerMixin中找到log方法开始执行,最终将日志写入文件subclasslog.txt