zoukankan      html  css  js  c++  java
  • Python之面向对象的组合、多态、菱形问题、子类中重用父类的两种方式

    一、组合

    '''
    1、什么是组合
        组合就是一个类的对象具备某一个属性,该属性的值是指向另外一个类的对象
    
    
    2、为何用组合
        组合也是用来解决类与类直接代码冗余问题的
    
    3、如何用组合
    
    '''
    # 继承减少代码冗余,但是将类与类进行了强耦合,python不崇尚,所以能不用继承就尽量不用继承
    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,stu_id):
            OldboyPeople.__init__(self,name,age,sex)
            self.stu_id=stu_id
    
        def choose_course(self):
            print('%s is choosing course' %self.name)
    
    class OldboyTeacher(OldboyPeople):
    
        def __init__(self, name, age, sex, level):
            OldboyPeople.__init__(self,name,age,sex)         #重用父类中的属性(功能)(减少代码冗余)------普通的函数传参,该传几个参数传几个参数
            self.level=level
    
        def score(self,stu,num):          #老师有修改学生成绩的功能,所以将学生对象和修改的成绩传入
            stu.score=num
            print('老师[%s]为学生[%s]打分[%s]' %(self.name,stu.name,num))
    
    
    stu1=OldboyStudent('猪哥',19,'male',1)
    tea1=OldboyTeacher('egon',18,'male',10)
    
    stu1.choose_course()          #学生对象选择课程------猪哥 is choosing course
    tea1.score(stu1,100)          #对象的绑定方法--------老师[egon]为学生[猪哥]打分[100]
    print(stu1.__dict__)          #查看学生的字典属性----{'name': '猪哥', 'age': 19, 'sex': 'male', 'stu_id': 1, 'score': 100}
    
    
    
    
    # #定制课程类
    class Course:                 #将学生和老师对象都有课程属性,所以将他们都有的属性抽出来,重新定义了一个课程类
        def __init__(self,name,period,price):           #调用类时自动触发
            self.name=name
            self.period=period
            self.price=price
        # 查看课程信息
        def tell_info(self):
            msg="""
            课程名:%s
            课程周期:%s
            课程价钱:%s
            """ %(self.name,self.period,self.price)
            print(msg)
    #定制人类--------老师和学生类都是人类
    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,stu_id):
            OldboyPeople.__init__(self,name,age,sex)     #类直接点类体代码的函数属性,在子类中重用父类功能,就是一个普通的函数
            self.stu_id=stu_id                           #子类派生出自己独有的数据属性
    
        def choose_course(self):                         #子类派生出自己独有的数据属性
            print('%s is choosing course' %self.name)
    
    class OldboyTeacher(OldboyPeople):
    
        def __init__(self, name, age, sex, level):
            OldboyPeople.__init__(self,name,age,sex)     #子类重用父类中的方法
            self.level=level                             #子类中派生出自己独有的属性
    
        def score(self,stu,num):                         #子类派生出自己独有的函数属性
            stu.score=num
            print('老师[%s]为学生[%s]打分[%s]' %(self.name,stu.name,num))
    
    # 创造课程----------------------------------对象
    python=Course('python全栈开发','5mons',3000)         #调用课程对象,产生课程对象,并自动触发__init__函数的执行
    linux=Course('linux运维','5mons',800)
    # python.tell_info()    #课程对象直接绑定课程类下的方法,并将课程对象当做第一个对象自动传入
    # linux.tell_info()
    
    
    # 创造学生与老师-----------------------------对象
    stu1=OldboyStudent('猪哥',19,'male',1)
    tea1=OldboyTeacher('egon',18,'male',10)
    
    # 重点
    # 将学生、老师与课程对象关联/组合-------------将对象之间进行关联
    # --------------------------关键-----------------------------------------------
    stu1.course=python          #---------------将学生对象stu1与课程对象python关联
    tea1.course=linux           #---------------将老师对象stu1与课程对象linux关联
    # --------------------------关键-----------------------------------------------
    '''组合就是一个类的对象具备某一个属性,该属性的值是指向另外一个类的对象'''
    # 即学生类产生的学生对象具备课程的属性,该课程的属性值是指向课程类中的课程对象的
    
    
    stu1.course.tell_info()     #--------------等价于python.tell_info(),会打印出学生的课程信息
    tea1.course.tell_info()     #--------------等价于linux.tell_info(),会打印出老师的课程信息

    二、菱形继承问题

    #coding:utf-8
    '''
    1、菱形继承
        当一个子继承多个父类时,多个父类最终继承了同一个类,称之为菱形继承
    
    2、菱形继承的问题:
        python2区分经典类与新式类,如果子的继承是一个菱形继承,那么经典类与形式的区别为?
            经典类下查找属性:深度优先查找-----------------一条道走到黑
            新式类下查找属性:广度优先查找------------------最后才去找菱形的端点
    '''
    
    class G(object):
        # def test(self):
        #     print('from G')
        pass
    
    class E(G):
        # def test(self):
        #     print('from E')
        pass
    
    class B(E):
        # def test(self):
        #     print('from B')
        pass
    
    class F(G):
        # def test(self):
        #     print('from F')
        pass
    
    class C(F):
        # def test(self):
        #     print('from C')
        pass
    
    class D(G):
        # def test(self):
        #     print('from D')
        pass
    
    class A(B,C,D):
        def test(self):
            print('from A')
        # pass
    
    obj=A()
    #C3算法只试用新式类,经典类不适用
    print(A.mro())  #mro是一种C3算法,[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
    obj.test()       #A->B->E-C-F-D->G-object-------对象属性的查找会严格按照mro列表的顺序进行查找

    三、在子类派生的新方法中重用父类功能的两种方式

    # 在子派生的新方法中重用父类功能的两种方式
    # 方式一:与继承无关
    #指名道姓法,直接用:类名.函数名
    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,stu_id):
            OldboyPeople.__init__(self,name,age,sex)      #-------(减少代码冗余)指名道姓法,直接用:类名.函数名,按照函数传参的规则进行传参即可
            self.stu_id=stu_id                            #子类派生出自己独有的数据属性
    
        def choose_course(self):                          #子类派生出自己的函数属性
            print('%s is choosing course' %self.name)
    
    
    # 方式二:严格以mro列表继承属性查找关系
    # super()会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)
    # super().__init__(不用为self传值)------------可以理解为super()是一个特殊的对象,所以对象绑定方法有一个自动传值的效果
    # 注意:
    # super的完整用法是super(自己的类名,self),在python2中需要写完整,而python3中可以简写为super()
    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,stu_id):
            # OldboyPeople.__init__(self,name,age,sex)         #------父类功能重用方法一
            super(OldboyStudent,self).__init__(name,age,sex)   #------父类功能重用方法二
            self.stu_id=stu_id
        #定制学生类的函数属性
        def choose_course(self):
            print('%s is choosing course' %self.name)
    
    stu1=OldboyStudent('猪哥',19,'male',1)
    print(stu1.__dict__)  #-------查看学生对象的字典属性,{'name': '猪哥', 'age': 19, 'sex': 'male', 'stu_id': 1}
    print(OldboyStudent.mro()) #------查看学生类的继承关系,[<class '__main__.OldboyStudent'>, <class '__main__.OldboyPeople'>, <class 'object'>]
    
    
    
    # 继承顺序查找再应用:
    class A:
        def f1(self):
            print('A.f1')
    class B:
        def f2(self):
            super().f1()         #B类和A类没有继承关系,但是会按照mro列表,从该类的下一个类继续进行查找,即此时会到A中进行查找
            print('B.f2')
    
    class C(B,A):
        pass
    
    obj=C()
    print(C.mro()) #C-》B->A->object,查看C类的继承关系,[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    obj.f2()       #对象属性的查找会严格按照mro列表的顺序进行查找,自己的对象没有到自己的类中找,自己的类中没有,到父类中找,此时父类查找的先后顺序就是按照mro列表来的
    
    # 打印结果:
    '''
    A.f1
    B.f2
    '''

    四、多态与多态性

    '''
    1 什么是多态
        多态指的是同一种事物的多种形态
            水-》冰、水蒸气、液态水
            动物-》人、狗、猪
    
    2 为和要用多态
        多态性:
        继承同一个类的多个子类中有相同的方法名
        那么子类产生的对象就可以不用考虑具体的类型而直接调用功能
    
    3 如何用
    '''
    import abc
    class Animal(metaclass=abc.ABCMeta): #定义一个抽象类,规范一个类的类(统一成一套标准)
        @abc.abstractmethod              #装饰器装饰后,意味着但凡继承类该类的方法,函数属性的属性名都必须与父类的函数属性名一致,否则就会报错
        def speak(self):
            pass
        @abc.abstractmethod              子类此时就必须与父类拥有相同的方法名,否则实例化就会报错
        def eat(self):
            pass
    
    # Animal() #强调:父类是用来制定标准的,不能被实例化-------此时实例化就会报错,这并不是python崇尚的,python更多的是一种约定俗成的,而不是硬性的限制
    class People(Animal):
        def speak(self):
            print('say hello')
    
        def eat(self):
            pass
    
    class Dog(Animal):
        def speak(self):
            print('汪汪汪')
    
        def eat(self):
            pass
    class Pig(Animal):
        def speak(self):
            print('哼哼哼')
    
        def eat(self):
            pass
    
    peo1=People()
    dog1=Dog()
    pig1=Pig()
    #
    #继承同一个动物类的多个子类(人、狗、猪类)中有相同的方法名(speakeat),那么子类产生的对象就可以不用考虑具体的类型而直接调用功能
    peo1.speak()      #不用考虑具体的对象类型,而直接调用speak的功能
    dog1.speak()
    pig1.speak()
    
    # -----------------------不用考虑是什么动物来调用speak的函数属性,所以将其封装成一个功能-----------------------------------
    def my_speak(animal):
        animal.speak()
    my_speak(peo1)
    my_speak(dog1)
    my_speak(pig1)
    # -----------------------不用考虑是什么动物来调用speak的函数属性,所以将其封装成一个功能-----------------------------------
    
    
    
    # 不同的数据类型,他们都有统计长度的这个方法,所以我们想到的是将他们制定成一套方法,便于对不同的数据类型进行统计
    l=[1,2,3]
    s='helllo'
    t=(1,2,3)
    
    print(l.__len__())          #用同一种形式去调用的好处就是将其功能封装成一个函数时,只需要传入对象就可以了
    print(s.__len__())          #如果用不同的方法名去统计不同数据类型的长度,这样封装成函数时就需要制定不同的函数去封装
    print(t.__len__())
    
    # def len(obj):
    #     return obj.__len__()
    
    print(len(l)) # l.__len__()
    print(len(s)) #s.__len__()
    print(len(t))
    
    
    
    # python推崇的是鸭子类型,只要你叫的声音像鸭子,并且你走路的样子也像鸭子,那你就是鸭子
    class Disk:
        def read(self):
            print('disk read')
    
        def write(self):
            print('disk wirte')
    
    
    class Process:
        def read(self):
            print('process read')
    
        def write(self):
            print('process wirte')
    
    
    class File:
        def read(self):
            print('file read')
    
        def write(self):
            print('file wirte')
    
    
    
    obj1=Disk()
    obj2=Process()
    obj3=File()
    
    
    
    obj1.read()
    obj1.write()
  • 相关阅读:
    Spring MVC(1)Spring MVC的初始化和流程以及SSM的实现
    Spring(四)Spring与数据库编程
    MyBatis(4)-- 动态SQL
    MyBatis(3)-- Mapper映射器
    MyBatis(2)-- MyBatis配置mybatis-config.xml
    MyBatis(1)-- MyBatis介绍
    计算机网络(2)-- URL、HTTP、HTTPS、HTML
    计算机网络(1)- TCP
    Shell脚本编程
    和为定值的多个数
  • 原文地址:https://www.cnblogs.com/sui776265233/p/9234603.html
Copyright © 2011-2022 走看看