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