• 面向对象进阶1


    类的继承

    什么是继承

    继承就是新建一个类的方式,新建的类我们叫子类,被继承的类称为父类

    在现实生活中,子女继承了父母的基因,遗产,所以子女会拥有父母的部分特征

    而在python中,子类继承父类就是子类完全拥有父类所具备的属性及技能

    为什么要继承

    比如我现在定义一个动物类,这个动物类拥有名字,身高,体重

    然后我再定义一个人类,这个人也有名字,身高,体重再加其他的属性技能

    我再定义一个狗类,也有名字,身高,体重再加其他的属性技能

    class Animal:
        
        def __init__(self,name,height,weight):
            self.name = name
            self.height = height
            self.weight = weight
            
            
    class People:
        
        def __init__(self,name,height,weight,age):
            self.name = name
            self.height = height
            self.weight = weight
            self.age = age
            
            
    class Dog:
        
        def __init__(self,name,height,weight,color):
            self.name = name
            self.height = height
            self.weight = weight
            self.color = color
    
    • 现在我们看上面的代码,会发现我们一直在写重复的代码,像人和狗都是属于动物类,他们在某些方面是具有动物共同特征的,所以可以继承动物类的特征
    class Animal:
        
        def __init__(self,name,height,weight):
            self.name = name
            self.height = height
            self.weight = weight
            
            
    class People(Animal):
        
        def __init__(self,name,height,weight,age):
            Animal.__init__(self,name,height,weight)
            self.age = age
            
            
    class Dog(Animal):
        
        def __init__(self,name,height,weight,color):
            Animal.__init__(self,name,height,weight)
            self.color = color
    
    • 这样看来我们的代码就精简的了汗多

    所以类的继承可以减少代码的冗余

    对象的继承

    • 使用__bases__可以获得对象继承的类
    print(People.__bases__)
    print(Dog.__bases__)
    
    (<class '__main__.Animal'>,)
    (<class '__main__.Animal'>,)
    
    • 在Python3中如果一个类没有继承任何类,则默认继承object类

    • 在Python2中如果一个类没有继承任何类,不会继承object类

    print(Animal.__bases__)
    
    (<class 'object'>,)
    

    类的查找顺序

    • 对象查找属性的顺序:对象自己--》对象的类--》父类--》父类
    class Foo:
        def f1(self):
            print('Foo.f1')
    
        def f2(self):
            print('Foo.f2')
            self.f1()   # 此时的self.f1()代表了obj.f1()
    
    
    class Bar(Foo):
        def f1(self):
            print('Bar.f1')
    
    
    # 对象查找属性的顺序:对象自己-》对象的类-》父类-》父类。。。
    obj = Bar()  # self是obj本身,即找到Bar的f1()
    obj.f2()
    
    Foo.f2
    Bar.f1
    

    类的派生

    派生:子类中新定义的属性的这个过程叫做派生,并且需要记住子类在使用派生的属性时始终以自己的为准

    派生方法一

    直接访问某一个函数,这种方式与继承无关

    class Animal:
        
        def __init__(self,name,height,weight):
            self.name = name
            self.height = height
            self.weight = weight
            
            
    class People:
        
        def __init__(self,name,height,weight,age):
            Animal.__init__(self,name,height,weight)
            self.age = age   # 派生
            
            
    class Dog(Animal):
        
        def __init__(self,name,height,weight,color):
            super().__init__(name,height,weight)
            self.color = color # 派生
    

    派生方法二

    • 严格以来继承属性查找关系

    • super()会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)

    • super().__init__(不用为self传值)

    • super的完整用法是super(自己的类名,self),在python2中需要写完整,而python3中可以简写为super()

    上面的Dog就是使用了方法二来进行的

    类的组合

    类的组合就是某一个类的对象具有一个属性,该属性的值指向另一个类的对象

    优点:用来解决类与类之间代码的冗余

    # 定义课程类
    class Course:
        def __init__(self,name,period,price):
            self.name = name
            self.period = period
            self.price = price
            
        def tell_info(self):
            print("""
            课程名:%s
            课程周期:%s
            课程价钱:%s"""%(self.name,self.period,self.price))
            
    # 定义成员共有属性        
    class OldBoyPeople:
        school = 'oldboy'
        
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
            
    # 定义老师类        
    class OldBoyTeacher(OldBoyPeople):
        def __init__(self,name,age,gender,level):
            super().__init__(name,age,gender)
            self.level = level
    
        def score(self,student,num):
            print(f'老师{self.name}为{student.name}的<{student.course.name}>课程打了{num}分')
            
    # 定义学生类        
    class OldBoyStudent(OldBoyPeople):
        
        def __init__(self,name,age,gender,stu_id):
            super().__init__(name,age,gender)
            self.stu_id = stu_id
            
        def choose_course(self,course):
            self.course = course
            print(f'{self.name}选择了<{course.name}>课程')
    
    # 创建课程
    python = Course('Python全栈开发','6个月',25000)
    python.tell_info()
    
            课程名:Python全栈开发
            课程周期:6个月
            课程价钱:25000
    
    linux = Course('Linux运维','6个月',20000)
    linux.tell_info()
    
            课程名:Linux运维
            课程周期:6个月
            课程价钱:20000
    
    # 创建老师
    tec = OldBoyTeacher('hades',27,'male',1)
    print(tec.__dict__)
    
    {'name': 'hades', 'age': 27, 'gender': 'male', 'level': 1}
    
    # 创建学生
    stu = OldBoyStudent('Bonnie',16,'female','00001')
    print(stu.__dict__)
    
    {'name': 'Bonnie', 'age': 16, 'gender': 'female', 'stu_id': '00001'}
    
    • 组合
    stu.choose_course(python)
    stu.course.tell_info()
    
    Bonnie选择了<Python全栈开发>课程
    
            课程名:Python全栈开发
            课程周期:6个月
            课程价钱:25000
    
    tec.score(stu,98)
    
    老师hades为Bonnie的<Python全栈开发>课程打了98分
    

    菱形继承问题

    类的分类

    新式类

    • 继承了object类以及该类的子类,都是新式类
    • Python3中所有的类都是新式类

    经典类

    • 没有继承了object类以及该类的子类,都是经典类
    • 只有Python2有经典类,因为Python2中的object类要手动进行添加

    菱形继承问题

    在python中子类可以继承多个父类,如A(B,C,D)

    如果继承关系为非菱形结构,则会按照(B,C,D)的顺序依次寻找,如果找不到就会报错

    菱形继承就是子类同时继承多个父类,而多个父类又同时继承了同一个父类

    这样就会有一个查找顺序的问题,属性查找顺序有两种:

    • 经典类下:深度优先,也就是一条线找到底
    • 新式类:广度优先,不找多各类最后继承的同一个类,直接去找下一个父类

    class G:
        def test(self):
            print('from G')
    
    class E(G):
        def test(self):
            print('from E')
            
    class F(G):
        def test(self):
            print('from F')
            
    class D(G):
        def test(self):
            print('from D')
            
    class C(F):
        def test(self):
            print('from C')
            
    class B(E):
        def test(self):
            print('from B')
            
    class A(B,C,D):
        def test(self):
            print('from A')
            
    obj = A()
    

    我们可以通过__mro__算法进行打印查找顺序

    print(A.__mro__)
    
    (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>)
    
    for i in A.__mro__:
        print(i)
    
    <class '__main__.A'>
    <class '__main__.B'>
    <class '__main__.E'>
    <class '__main__.C'>
    <class '__main__.F'>
    <class '__main__.D'>
    <class '__main__.G'>
    <class 'object'>
    

    为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

    而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

    1. 子类会先于父类被检查

    2. 多个父类会根据它们在列表中的顺序被检查

    3. 如果对下一个类存在两个合法的选择,选择第一个父类

    类的多态与多态性

    多态

    多态指的是一类事物具有多种形态,即一个抽象的类具有多个子类,因而多态的概念依赖于继承

    1. 数据类型有多种形态:字符串,列表,整型
    2. 动物有多种形态:人,猪,狗
    class Animal:
        def communicate(self):   # 子类约定俗称的必须实现这个方法
            print('动物类会交流')
            
    class People(Animal):
        def communicate(self):
            print('人类通过语言交流')
            
    class Dog(Animal):
        def communicate(self):
            print('狗通过"汪汪汪"进行交流')
            
    class Pig(Animal):
        def communicate(self):
            print('猪通过"哼哼哼"进行交流')
    
    peo1 = People()
    dog1 = Dog()
    pig1 = Pig()
    
    peo1.communicate()
    dog1.communicate()
    pig1.communicate()
    
    人类通过语言交流
    狗通过"汪汪汪"进行交流
    猪通过"哼哼哼"进行交流
    

    但是总有人不遵守规则,想搞例外,所以我们可以通过@abc.abstractmethod方法进行强制要求,如果子类没有这个功能则会报错

    import abc
    
    class Animal(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def communicate(self):   # 子类约定俗称的必须实现这个方法
            print('动物类会交流')
            
    class People(Animal):
        def communicate(self):
            print('人类通过语言交流')
            
    class Dog(Animal):
        def communicate(self):
            print('狗通过"汪汪汪"进行交流')
            
    class Pig(Animal):
    #     def communicate(self):
    #         print('猪通过"哼哼哼"进行交流')
        pass 
    
    peo2 = People()
    peo2.communicate()
    dog2 = Dog()
    dog2.communicate()
    pig2 = Pig() # 报错
    
    
    人类通过语言交流
    狗通过"汪汪汪"进行交流
    
    
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-76-4c019fadd2e5> in <module>
         23 dog2 = Dog()
         24 dog2.communicate()
    ---> 25 pig2 = Pig() # 报错
    
    
    TypeError: Can't instantiate abstract class Pig with abstract methods communicate
    

    多态性

    多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

    def func(obj):
        obj.communicate()
        
    func(peo1)
    func(dog1)
    func(pig1)
    
    人类通过语言交流
    狗通过"汪汪汪"进行交流
    猪通过"哼哼哼"进行交流
    
    func(peo2)
    func(dog2)
    
    人类通过语言交流
    狗通过"汪汪汪"进行交流
    

    综上可以说,多态性是一个接口(函数func)的多种实现(如obj.communicate())

    多态性的好处

    1. 增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

    2. 增加了程序额可扩展性:通过继承Animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

  • 相关阅读:
    Python学习笔记:pandas.read_csv分块读取大文件(chunksize、iterator=True)
    Python学习笔记:os.stat().st_size、os.path.getsize()获取文件大小
    7-1 打印沙漏
    7-1 币值转换
    7-1 抓老鼠啊~亏了还是赚了?
    第四周编程总结哦也
    2018秋寒假作业6—PTA编程总结3
    PTA编程总结3
    PTA编程总结1
    秋季学期学习总结
  • 原文地址:https://www.cnblogs.com/Hades123/p/11061331.html
Copyright © 2020-2023  润新知