面向对象+继承

内容概要

  • 面向对象介绍
  • 实现面向对象编程
  • 属性查找
  • 封装
  • 选课
  • property装饰器
  • 继承
  • 多继承带来的菱形问题
  • 单继承背景下的属性查找
  • classmethod
  • mixins机制
  • 在子类派生的新方法中如何重用父类的功能
  • 多态

内容详细

  • 面向对象介绍

    '''
    面向过程
        核心是'过程'二字
        过程的终极奥义就是将程序流程化
        过程是'流水线',用来分步骤解决的
    
    面向对象
        核心是'对象'二字
        对象的终极奥义就是将程序'整合'
        对象是'容器',用来盛放数据和功能的
    
        类也是'容器',该容器用来存放同类对象共有的数据和功能
    '''
    
    # 程序 = 数据+功能
    
  • 实现面向对象编程

    # 先定义类
    # 类是对象相似数据与功能的集合体
    # 所以类体中最常见的是变量与函数的定义,但是类体其实是可以包含任意其他代码的
    # 注意:类体代码是在类定义阶段就会立即执行的,会产生类的名称空间
    
    # 再调用类产出对象
    
    # # 一、先定义类
    # class Student:
    #     # 1.变量的定义:
    #     stu_school = 'oldboy'
    #
    #     # 2.功能的定义
    #     def tell_stu_info(stu_obj):
    #         print('学生信息:名字:%s 年龄:%s 性别:%s '%(
    #             stu_obj['stu_name'],
    #             stu_obj['stu_age'],
    #             stu_obj['stu_gender']
    #         ))
    #
    #     def set_info(stu_obj,x,y,z):
    #         stu_obj['stu_name'] = x
    #         stu_obj['stu_age'] = y
    #         stu_obj['stu_gender'] = z
    # print(Student.__dict__)
    # # 属性访问的语法
    # # 1.访问数据属性
    # print(Student.stu_school)  # print(Student.__dict__['stu_school'])
    # # 2.访问函数属性
    # print(Student.set_info)  # print(Student.__dict__['set_info'])
    
    
    # 二、再调用类产生对象
    # stu1_obj = Student()
    # stu2_obj = Student()
    # stu3_obj = Student()
    
    # 为对象定制自己独有的属性
    # 问题1:代码重复
    # 问题2:属性的查找顺序
    # stu1_obj.stu_name = 'ycc'
    # stu1_obj.stu_age = 18
    # stu1_obj.stu_gender = 'male'
    # print(stu1_obj.__dict__)
    #
    # stu2_obj.stu_name = 'xixi'
    # stu2_obj.stu_age = 19
    # stu2_obj.stu_gender = 'female'
    # print(stu2_obj.__dict__)
    #
    #
    # stu3_obj.stu_name = 'jason'
    # stu3_obj.stu_age = 18
    # stu3_obj.stu_gender = 'male'
    # print(stu3_obj.__dict__)
    
    # 解决问题一:
    # def init(obj,stu_name,stu_age,stu_gender):
    #     obj.stu_name = stu_name
    #     obj.stu_age = stu_age
    #     obj.stu_gender = stu_gender
    #
    # init(stu1_obj,'ycc',18,'male')
    # init(stu2_obj,'xixi',19,'female')
    # init(stu3_obj,'jason',20,'male')
    # print(stu1_obj.__dict__)
    # print(stu2_obj.__dict__)
    # print(stu3_obj.__dict__)
    
    # 二、解决问题二:
    # 1、先定义类
    class Student:
        # 1.变量的定义:
        stu_school = 'oldboy'
    
        #           空对象,'ycc',    18,     'male'
        def __init__(obj,stu_name,stu_age,stu_gender):
            obj.stu_name = stu_name  # 空对象.stu_name = 'ycc'
            obj.stu_age = stu_age  # 空对象.stu_age = 18
            obj.stu_gender = stu_gender  # 空对象.stu_gender = 'male'
    
        # 2.功能的定义
        def tell_stu_info(stu_obj):
            print('学生信息:名字:%s 年龄:%s 性别:%s '%(
                stu_obj['stu_name'],
                stu_obj['stu_age'],
                stu_obj['stu_gender']
            ))
    
        def set_info(stu_obj,stu_name,stu_age,stu_gender):
            stu_obj['stu_name'] = stu_name
            stu_obj['stu_age'] = stu_age
            stu_obj['stu_gender'] = stu_gender
    
    
    # 2、再调用类产生对象
    # 调用类的过程又称之为实例化,发生了三件事
    # 1、先产生一个空对象
    # 2、python会自动调用类中的__init__方法将空对象已经调用类时括号内传入的参数一同传给__init__方法
    # 3.返回初始完的对象
    stu1_obj = Student('ycc',18,'male')  # Student.__init(空对象,'ycc',18,'male')
    stu2_obj = Student('xixi',19,'female')
    stu3_obj = Student('jason',20,'male')
    
    print(stu1_obj.__dict__)
    print(stu2_obj.__dict__)
    print(stu3_obj.__dict__)
    
    # 总结__init__方法
    # 1、会在调用类时自动触发执行,用来为对象初始化自己独有的数据
    # 2、__init__内应该存放是为对象初始化属性的功能,但是是可以存放任意其他代码,
    # 想要在类调用时就立刻执行的代码都可以放到该方法内
    # 3、__init__方法必须返回None
    
  • 属性查找

    class Student:
        # 1.变量的定义:
        stu_school = 'oldboy'
    
        #           空对象,'ycc',    18,     'male'
        def __init__(self,stu_name,stu_age,stu_gender):
            self.stu_name = stu_name  # 空对象.stu_name = 'ycc'
            self.stu_age = stu_age  # 空对象.stu_age = 18
            self.stu_gender = stu_gender  # 空对象.stu_gender = 'male'
    
        # 2.功能的定义
        def tell_stu_info(self):
            print('学生信息:名字:%s 年龄:%s 性别:%s '%(
                # stu_obj['stu_name'],
                # stu_obj['stu_age'],
                # stu_obj['stu_gender']
                self.stu_name,
                self.stu_age,
                self.stu_gender
            ))
    
        def set_info(self,stu_name,stu_age,stu_gender):
            # stu_obj['stu_name'] = stu_name
            # stu_obj['stu_age'] = stu_age
            # stu_obj['stu_gender'] = stu_gender
            self.stu_name = stu_name
            self.stu_age = stu_age
            self.stu_gender = stu_gender
    
        def choose(self,x):
            print('正在选课')
            self.course = x
    
    stu1_obj = Student('ycc',18,'male')
    stu2_obj = Student('xixi',19,'female')
    stu3_obj = Student('jason',20,'male')
    
    # 类中存放的是对象共有的数据和功能
    # 一、类可以访问:
    # 1.类的数据属性
    # print(Student.stu_school)
    # # 2.类的函数属性
    # print(Student.set_info)
    # print(Student.tell_stu_info)
    
    # 二、但其实类中的东西是给对象用的
    # 1、类的数据属性是共享给所有对象用的,大家访问的地址都一样
    # print(stu1_obj.stu_name)
    # print(stu1_obj.stu_age)
    # print(stu1_obj.stu_gender)
    # print(stu1_obj.stu_school)
    # 查找顺序,先在对象内找,让后去类里找
    
    # print(Student.stu_school)  # oldboy
    # print(stu1_obj.stu_school)  # oldboy
    # print(stu2_obj.stu_school)  # oldboy
    # print(stu3_obj.stu_school)  # oldboy
    #
    # print(id(Student.stu_school))  # 1464557583344
    # print(id(stu1_obj.stu_school))  # 1464557583344
    # print(id(stu2_obj.stu_school))  # 1464557583344
    # print(id(stu3_obj.stu_school))  # 1464557583344
    
    # Student.stu_school = 'OLDBOY'
    # print(Student.stu_school)  # OLDBOY
    # print(stu1_obj.stu_school)  # OLDBOY
    # print(stu2_obj.stu_school)  # OLDBOY
    # print(stu3_obj.stu_school)  # OLDBOY
    
    # stu1_obj.stu_school = 'OLDBOY'
    # print(Student.stu_school)  # oldboy
    # print(stu1_obj.stu_school)  # OLDBOY
    # print(stu2_obj.stu_school)  # oldboy
    # print(stu3_obj.stu_school)  # oldboy
    # 查找顺序先对象后类,这里直接在对象内新建了一个
    
    # # 2.类的函数属性是绑定给对象用的
    # print(Student.tell_stu_info)
    # print(Student.set_info)
    #
    #
    # Student.tell_stu_info(stu1_obj)
    # Student.tell_stu_info(stu2_obj)
    # Student.tell_stu_info(stu3_obj)
    # # 学生信息:名字:ycc 年龄:18 性别:male
    # # 学生信息:名字:xixi 年龄:19 性别:female
    # # 学生信息:名字:jason 年龄:20 性别:male
    #
    # Student.set_info(stu1_obj,'YCC',20,'male')
    # Student.tell_stu_info(stu1_obj)
    # # 学生信息:名字:YCC 年龄:20 性别:male
    
    # 绑定方法的特殊之处在于:谁来调用绑定方法就会将谁当作第一个参数自动传入
    # stu1_obj.tell_stu_info()
    # stu2_obj.tell_stu_info()
    # stu3_obj.tell_stu_info()
    # 学生信息:名字:ycc 年龄:18 性别:male
    # 学生信息:名字:xixi 年龄:19 性别:female
    # 学生信息:名字:jason 年龄:20 性别:male
    
    
    # stu1_obj.choose('python')
    # print(stu1_obj.course)
    # stu2_obj.choose('linux')
    # print(stu2_obj.course)
    # stu3_obj.choose('高级架构师')
    # print(stu3_obj.course)
    
    # 例:
    l1 = ['aa','bb','cc']
    l2 = [11,22,33]
    print(l1.append('dd'))
    # print(l1)
    # print(list.append(l1))  # list.append() takes exactly one argument (0 given)
    
    # list.append(l1,'dd')  # ['aa', 'bb', 'cc', 'dd']
    # list.append(l2,'dd')  # [11, 22, 33, 'dd']
    # print(l1)
    # print(l2)
    
  • 封装

    # 一、封装的概念
    # 封装是面向对象三大特性的最核心的一个特性
    # 封装<->整合
    
    # 二、封装的属性进行隐藏
    # 1)如何隐藏:在属性名前加__前缀,就会实现对外隐藏属性效果
    # 该隐藏需要注意的问题:
    # 1、在类外部无法直接访问双下滑线开头的属性,
    # 但知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,
    # 如Foo._A__N,所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。
    # class Foo:
    #     __x = 2  # _Foo__x
    #
    #     def __f1(self):  # _Foo__f1
    #         print('from f1')
    #
    #
    # print(Foo.__dict__)  # 可以打印出隐藏属性
    # print(Foo._Foo__x)  # 虽然能,但是不要在外部这样访问
    # print(Foo._Foo__f1)
    
    
    # 2、这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法的时候统一发生变形
    # class Foo:
    #     __x = 2  # _Foo__x = 1  # 在检查语法的时候,这里会直接发生变形,下面也是如此,统一发生形变
    #
    #     def __f1(self):  # _Foo__f1
    #         print('from f1')
    #
    #     def f2(self):
    #         print(self.__x)  # print(self._Foo__x)
    #         print(self.__f1)  # print(self._Foo__f1) # 因此这里能访问到上面
    #
    # obj = Foo()
    # obj.f2()
    
    # 3.这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头属性都不会变形
    # class Foo:
    #     __x = 2
    #
    #     def __f1(self):
    #         print('from f1')
    #
    #     def f2(self):
    #         print(self.__x)
    #         print(self.__f1)
    #
    # Foo.__y = 3  # 只在检测类体语法的时候发生一次
    # print(Foo.__dict__)  # 可以看到__y根本没变形
    # print(Foo.__y)  # 3
    
    
    # 对象隐藏也一样
    # 1.
    # class Foo:
    #     def __init__(self,name,age):
    #         self.name = name
    #         self.age = age
    #
    # obj = Foo('ycc',18)
    # print(obj.name,obj.age)  # ycc 18
    
    # 2.
    # class Foo:
    #     def __init__(self,name,age):
    #         self.__name = name
    #         self.__age = age
    #
    # obj = Foo('ycc',18)
    # print(obj.name,obj.age)  # 报错
    
    # 3.
    # class Foo:
    #     def __init__(self,name,age):
    #         self.__name = name
    #         self.__age = age
    #
    # obj = Foo('ycc',18)
    # print(obj.__dict__)  # {'_Foo__name': 'ycc', '_Foo__age': 18}
    # print(obj._Foo__name,obj._Foo__age)  # ycc 18
    
    
    # 2)为何隐藏
    # 1.隐藏数据属性:将数据隐藏起来就限制了类外部对数据的直接操作,
    # 然后类内应该提供相应的接口来允许类外部间接地操作数据,
    # 接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
    # 设计者:
    class People:
        def __init__(self,name):
            self.__name = name
    
        def get_name(self):
            print(self.__name)
    
        def set_name(self,val):
            # 设计者可以再这附加任意逻辑
            if type(val) is not str:
                print('小垃圾,必须传字符串类型')
                return
            self.__name= val
    
    obj = People('egon')
    # print(obj.name) # 无法直接使用
    obj.set_name('EGON')
    obj.get_name()  # egon
    
    # 2.隐藏函数/方法属性:目的是为了隔离复杂度
    '''
    >>> class ATM:
    ...     def __card(self): #插卡
    ...         print('插卡')
    ...     def __auth(self): #身份认证
    ...         print('用户认证')
    ...     def __input(self): #输入金额
    ...         print('输入取款金额')
    ...     def __print_bill(self): #打印小票
    ...         print('打印账单')
    ...     def __take_money(self): #取钱
    ...         print('取款')
    ...     def withdraw(self): #取款功能
    ...         self.__card()
    ...         self.__auth()
    ...         self.__input()
    ...         self.__print_bill()
    ...         self.__take_money()
    ...
    >>> obj=ATM()
    >>> obj.withdraw()
    '''
    
    
    
    
  • 选课

    # 整合-》解耦合-》扩展性增强
    # 组合 在一个类中以另外一个类的对象作为数据属性,称为类的组合。组合与继承都是用来解决代码的重用性问题
    class School:
        school_name = 'OLDBOY'
    
        def __init__(self, nickname, address):
            self.nickname = nickname
            self.address = address
            self.classes = []
    
        def related_class(self, class_obj):
            self.classes.append(class_obj)
    
        def tell_class(self):
            print(self.nickname.center(60,'='))
            for class_obj in self.classes:
                class_obj.tell_course()
    
    
    # 一、学校
    # 1、创建校区
    school_obj1 = School('老男孩魔都校区', '上海')
    school_obj2 = School('老男孩帝都校区', '北京')
    
    # 2、为学校开设班级
    # school_obj1.related_class('脱产18期')
    # school_obj1.related_class('脱产19期')
    # for class_name in school_obj1.classes:
    #     print('%s %s'%(school_obj1.nickname,class_name))
    
    # school_obj2.related_class('脱产20期')
    # for class_name in school_obj2.classes:
    #     print('%s %s'%(school_obj2.nickname,class_name))
    
    # 查看每个校区开设的班级
    # school_obj1.tell_class()
    # school_obj2.tell_class()
    
    
    class Class:
        def __init__(self, name):
            self.name = name
            self.course = None
    
        def class_related(self, course_obj):
            self.course = course_obj
    
    
        def tell_course(self):
            print('班级名:%s'%self.name,end=' ')
            self.course.tell_info()
    
    
    # 二、班级
    # 1.创建班级
    class_obj1 = Class('脱产18期')
    class_obj2 = Class('脱产19期')
    class_obj3 = Class('脱产20期')
    
    # 2.为班级关联一个课程
    # class_obj1.class_related('python全栈开发')
    # class_obj1.class_related('linux运维')
    # class_obj2.class_related('高级架构师')
    # class_obj3.class_related('python全栈开发')
    
    # 3.查看班级开设的课程信息
    # print('%s %s'%(class_obj1.name,class_obj1.courses))
    # print('%s %s'%(class_obj2.name,class_obj2.courses))
    # class_obj1.tell_course()
    # class_obj2.tell_course()
    # class_obj3.tell_course()
    
    # 4.为学校开设班级
    # 上海校区开了:脱产14期  上海校区开了:脱产15期
    school_obj1.related_class(class_obj1)
    school_obj1.related_class(class_obj1)
    
    # 北京校区开了:
    school_obj2.related_class(class_obj2)
    
    
    # school_obj1.tell_class()
    # school_obj2.tell_class()
    
    
    class Course:
        def __init__(self,name,period,price):
            self.name = name
            self.period = period
            self.price = price
    
        def course_related(self):
            pass
    
        def tell_info(self):
            print('课程名:%s 课程周期:%s 课程价格:%s '%(self.name,self.period,self.price))
    
    course_obj1 = Course('python全栈开发','6months',20000)
    course_obj2 = Course('linux运维','5months',18000)
    course_obj3 = Course('高级架构师','7months',21000)
    
    
    # course_obj1.tell_info()
    # course_obj2.tell_info()
    # course_obj3.tell_info()
    
    class_obj1.class_related(course_obj1)
    class_obj1.class_related(course_obj2)
    class_obj2.class_related(course_obj3)
    class_obj3.class_related(course_obj1)
    
    # class_obj1.tell_course()
    # class_obj2.tell_course()
    # class_obj3.tell_course()
    
    school_obj1.tell_class()
    school_obj2.tell_class()
    
    
    
    
    class Student:
        pass
    
    
  • property装饰器

    # 装饰器是在不修改被装饰对象的源代码以及调用方式的情况下,为被装饰对象添加新功能的可调用对象
    # print(property)  # <class 'property'>
    
    # property是一个装饰器,是用来绑定给对象的方法伪装成一个数据属性
    
    # 案例一:
    # class People:
    #     def __init__(self,name,height,weight):
    #         self.name = name
    #         self.height = height
    #         self.weight = weight
    #
    #     # 定义函数的原因1:
    #     # 1.从bmi的公式来看,bmi应该是出发功能计算得到的
    #     # 2.bmi是随着升高、体重的变化而动态变化的,不是一个固定的值,说白了,每次都是要临时计算得到的
    #
    #     # 而bmi听起来更像一个数据属性,而非功能
    #     @property
    #     def bmi(self):
    #         return self.weight / (self.height ** 2)
    #
    # obj = People('ycc',1.83,90)
    # # print(obj.bmi())
    # print(obj.bmi)  # 加了装饰器之后,就不用再加()了
    
    # # 案例二:
    # class People:
    #     def __init__(self,name):
    #         self.__name = name
    #
    #
    #     def get_name(self):
    #         return self.__name
    #
    #     def set_name(self,val):
    #         if type(val) is not str:
    #             print('必须传入str类型')
    #             return
    #         self.__name = val
    #
    #     def del_name(self):
    #         print("不让删除")
    #
    #     name = property(get_name,set_name,del_name)
    # obj = People('ycc')
    # print(obj.name)  # ycc
    # obj.name = 'YCC'
    # print(obj.name)  # YCC
    # del obj.name  # 不让删除
    
    # 案例三:
    class People:
        def __init__(self,name):
            self.__name = name
        # 查删改和上面效果一样
        @property
        def name(self):
            return self.__name
    
        @name.setter  # 固定用法
        def name(self,val):
            if type(val) is not str:
                print('必须传入str类型')
                return
            self.__name = val
    
        @name.deleter
        def name(self):
            print("不让删除")
    
        # name = property(get_name,set_name,del_name)
    obj = People('ycc')
    print(obj.name)  # ycc
    obj.name = 'YCC'
    print(obj.name)  # YCC
    del obj.name  # 不让删除
    
    
  • 继承

    # 1.什么是继承
    # 1)继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性
    # 2)需要注意的是:python支持多继承
    #     在python中,新建的类可以继承一个或多个父类,在其他语言中是没有多继承的
    
    # class Parent1:
    # # class Parent1(object): 这样使其也变成新式类,在python2中也能使用
    #
    #     pass
    #
    # class Parent2:
    # # class Parent1(object):
    #     pass
    #
    # class Sub1(Parent1):
    #     pass
    #
    # class Sub2(Parent1,Parent2):
    #     pass
    
    
    # print(Sub1.__bases__)  # (<class '__main__.Parent1'>,)
    # print(Sub2.__bases__)  # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
    
    # ps1:在python2中有经典类和新式类之分
    # 新式类:继承了object类的子类,以及该子类的子类子子类
    # 经典:没有继承object类的子类,以及该子类的子类子子类
    
    # ps2:在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类
    # print(Parent1.__bases__)  # (<class 'object'>,)
    # print(Parent2.__bases__)  # (<class 'object'>,)
    
    # 3)python的多继承
    #   优点:子类可以同时遗传多个父类的属性,最大限度的重用代码
    #   缺点:
    #         1.违背了人的思维习惯:继承表达的是一种什么'是'什么的关系
    #         2.代码的可读性变差
    #         3.不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差
    #             如果真的涉及到一个子类不可避免的要重用多个父类的属性,应该使用Mixins
    # 2.为何要用继承:用来解决代码冗余问题
    
    
    # 如何实现继承
    # 示范一:类与类之间出现了代码冗余的问题
    # class Student:
    #     school = 'oldschool'
    #     def __init__(self,name ,age,gender):
    #         self.name = name
    #         self.age = age
    #         self.gender = gender
    #
    #     def choose_course(self):
    #         print('学生%s 正在选课 '% self.name)
    #
    # class Teacher:
    #     school = 'oldboy'
    #     def __init__(self,name,age,gender,salary,level):
    #         self.name = name
    #         self.age = age
    #         self.gender = gender
    #         self.salary = salary
    #         self.level = level
    #
    #     def score(self):
    #         print('老师%s 正在打分'%self.name)
    
    # 示范二:基于继承解决类与类之间代码冗余的问题
    class OldboyPeopel:
        school = 'oldschool'
    
        def __init__(self,name ,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    
    class Student(OldboyPeopel):
        # school = 'oldschool'
        # def __init__(self,name ,age,gender):
        #     self.name = name
        #     self.age = age
        #     self.gender = gender
    
        def choose_course(self):
            print('学生%s 正在选课 '% self.name)
    
    stu_obj = Student('ycc',18,'male')
    print(stu_obj.__dict__)  # {'name': 'ycc', 'age': 18, 'gender': 'male'}
    print(stu_obj.school)  # oldschool
    stu_obj.choose_course()
    
    class Teacher(OldboyPeopel):
        # school = 'oldboy'
        def __init__(self,name,age,gender,salary,level):
            # self.name = name
            # self.age = age
            # self.gender = gender
            OldboyPeopel.__init__(self,name,age,gender)
            self.salary = salary
            self.level = level
    
        def score(self):
            print('老师%s 正在打分'%self.name)
    
    tea_obj = Teacher('egon',18,'male',3000,10)
    print(tea_obj.__dict__)
    print(tea_obj.name)
    tea_obj.score()
    
  • 多继承带来的菱形问题

    # 一、菱形问题介绍与MRO
    # class A:
    #     def test(self):
    #         print('from A')
    # class B(A):
    #     def test(self):
    #         print('from B')
    # class C(A):
    #     def test(self):
    #         print('from C')
    # class D(B,C):
    #     pass
    
    # print(D.mro())  # 类D以及类D的对象访问属性都是参照该类的mro列表
    # #[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>,
    # # <class 'object'>]
    #
    # obj = D()
    # obj.test()
    # # from B
    # # 查找顺序:首先先在D类里找,D类没有去其父类里找,但是这里多继承,因此通过print(D.mro()),来查看顺序
    
    # print(C.mro())  # 类D以及类D的对象访问属性都是参照该类的mro列表
    # # [<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
    # obj = C()
    # obj.test()  # from C
    
    # 总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro
    
    # 二、如果多继承是非菱形继承,经典类与新式类的属性查找顺序一样:
    #       都是一个分支一个分支的找下去,然后最后找object
    
    # class E:
    #     def test(self):
    #         print('from E')
    #
    # class F:
    #     def test(self):
    #         print('from F')
    #
    # class B(E):
    #     def test(self):
    #         print('from B')
    #
    # class C(F):
    #     def test(self):
    #         print('from C')
    #
    # class D:
    #     def test(self):
    #         print('from D')
    #
    # class A(B, C, D):
    #     # def test(self):
    #     #     print('from A')
    #     pass
    #
    # print(A.mro())
    # # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>,
    # # <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
    #
    # obj = A()
    # obj.test() # 结果为:from B
    
    
    # 三、如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样
    # 新式类:广度优先,会在检索最后一条分支的时候检索大脑袋(共同的父类)
    class G(object):
        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 B(E):
        def test(self):
            print('from B')
    
    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
    
    obj = A()
    obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object
    # 可依次注释上述类中的方法test来进行验证
    
    # 经典类:深度优先,会在检索第一条分支的时候就直接一条道走道黑,即会检索大脑袋(共同的父类)
    class G: # 在python2中,未继承object的类及其子类,都是经典类
        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 B(E):
        def test(self):
            print('from B')
    
    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
    
    obj = A()
    obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object
    # 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试
    
    
    
    # 总结:
    # 多继承到底要不要用
    # 要用,但是规避几点问题:
    # 1、继承结构尽量不要过于复杂
    # 2、推荐使用mixins机制:在多继承的背景下满足继承的什么'是'什么的关系
    
  • 单继承背景下的属性查找

    # 单继承背景下的属性查找# 示范一、# class Foo:#     def f1(self):#         print('Foo.f1')##     def f2(self):#         print('FOO.f2')#         self.f1() # obj.f1()## class Bar(Foo):#     def f1(self):#         print('Bar.f1')## obj = Bar()# obj.f2()# FOO.f2# Bar.f1# # 示范二、# class Foo:#     def f1(self):#         print('Foo.f1')##     def f2(self):#         print('FOO.f2')#         Foo.f1(self)  # 调用当前类中的f1### class Bar(Foo):#     def f1(self):#         print('Bar.f1')## obj = Bar()# obj.f2()# # FOO.f2# # Foo.f1# 示范三、# class Foo:#     def __f1(self):#         print('Foo.f1')##     def f2(self):#         print('FOO.f2')#         self.__f1()## class Bar(Foo):#     def f1(self):#         print('Bar.f1')## obj = Bar()# obj.f2()# FOO.f2# Foo.f1
    
  • classmethod

    # 一、绑定方法:特殊之处在于将调用者本身当做第一个参数自动传入#   1.绑定给对象的方法:调用者是对象,自动传入的是对象#   2.绑定给类的方法:调用者是类,自动传入的是类# import settings# class Mysql:#     def __init__(self,ip,port):#         self.ip = ip#         self.port = port##     def func(self):#         print('%s %s'%(self.ip,self.port))## # obj = Mysql('192.168.1.1',80)#     @classmethod#     def from_conf(cls):  # 将下面的函数装饰成绑定给类的方法#         print(cls)  # <class '__main__.Mysql'>  将其写活了#         return cls(settings.IP,settings.PORT)# obj1 = Mysql.from_conf()# print(obj1.__dict__)# print(obj1.port)# 二、非绑定方法==>静态方法#   没有绑定给任何人:调用者可以是类、对象,没有自动传参的效果class Mysql:    def __init__(self,ip,port):        self.nid = self.create_id()        self.ip = ip        self.prot = port    @staticmethod  # 将下述函数装饰成一个静态方法    def create_id():        # print(x,y,z)        import uuid        return uuid.uuid4()    @classmethod    def f1(cls):        pass    def f2(self):        passobj = Mysql('192.168.1.1',3306)# Mysql.create_id()# obj.create_id()# print(Mysql.create_id)  # <function Mysql.create_id at 0x000002BA70B4B550># print(obj.create_id)  # <function Mysql.create_id at 0x000002BA70B4B550># print(Mysql.create_id)  # <function Mysql.create_id at 0x00000178B738B550># print(Mysql.f1)  # <bound method Mysql.f1 of <class '__main__.Mysql'>># print(Mysql.f2)  # <function Mysql.f2 at 0x00000178B738BAF0>
    
  • mixins机制

    # 1、多继承的正确打开方式:mixins机制# mixins的机制核心:就是在多继承的背景下尽可能的提升多继承的可读性'''可以看到,上面的CivilAircraft、Helicopter类实现了多继承,不过它继承的第一个类我们起名为FlyableMixin,而不是Flyable,这个并不影响功能,但是会告诉后来读代码的人,这个类是一个Mixin类,表示混入(mix-in),这种命名方式就是用来明确地告诉别人(python语言惯用的手法),这个类是作为功能添加到子类中,而不是作为父类,它的作用同Java中的接口。所以从含义上理解,CivilAircraft、Helicopter类都只是一个Vehicle,而不是一个飞行器。使用Mixin类实现多重继承要非常小心首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类然后,它不依赖于子类的实现最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)​ Mixins是从多个类中重用代码的好方法,但是需要付出相应的代价,我们定义的Minx类越多,子类的代码可读性就会越差,并且更恶心的是,在继承的层级变多时,代码阅读者在定位某一个方法到底在何处调用时会晕头转向'''class Vehicle:  # 交通工具    passclass FlyableMixin:    def fly(self):        '''        飞行功能相应的代码        '''        print("I am flying")class CivilAircraft(FlyableMixin, Vehicle):  # 民航飞机    passclass Helicopter(FlyableMixin, Vehicle):  # 直升飞机    passclass Car(Vehicle):  # 汽车    pass# ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路
    
  • 在子类派生的新方法中如何重用父类的功能

    # 在子类派生的新方法中如何重用父类的功能# 方式一:指名道姓调用某一个类下的函数=》不依赖于继承关系# class OldboyPeopel:#     school = 'oldschool'##     def __init__(self,name ,age,gender):#         self.name = name#         self.age = age#         self.gender = gender## class Student(OldboyPeopel):##     def choose_course(self):#         print('学生%s 正在选课 '% self.name)## stu_obj = Student('ycc',18,'male')# print(stu_obj.__dict__)  # {'name': 'ycc', 'age': 18, 'gender': 'male'}# print(stu_obj.school)  # oldschool# stu_obj.choose_course()## class Teacher(OldboyPeopel):#     def __init__(self,name,age,gender,salary,level):#         OldboyPeopel.__init__(self,name,age,gender)#         self.salary = salary#         self.level = level##     def score(self):#         print('老师%s 正在打分'%self.name)## tea_obj = Teacher('egon',18,'male',3000,10)# print(tea_obj.__dict__)# print(tea_obj.name)# tea_obj.score()# # 方式二:super()调用父类提供给自己的方法=》严格以来继承体系#        调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性# class OldboyPeopel:#     school = 'oldschool'##     def __init__(self,name ,age,gender):#         self.name = name#         self.age = age#         self.gender = gender## class Student(OldboyPeopel):##     def choose_course(self):#         print('学生%s 正在选课 '% self.name)## # stu_obj = Student('ycc',18,'male')# # print(stu_obj.__dict__)  # {'name': 'ycc', 'age': 18, 'gender': 'male'}# # print(stu_obj.school)  # oldschool# # stu_obj.choose_course()## class Teacher(OldboyPeopel):#     def __init__(self,name,age,gender,salary,level):#         # super(Teacher, self).__init__(name,age,gender)#         super().__init__(name,age,gender)  # super()只能在新式类中使用,可以简写#         # 调用的是方法,自动传入对象self#         self.salary = salary#         self.level = level##     def score(self):#         print('老师%s 正在打分'%self.name)# print(Teacher.mro())# # [<class '__main__.Teacher'>, <class '__main__.OldboyPeopel'>, <class 'object'>]# # 这里可以看到可以从类OldboyPeopel找name,age,gender# tea_obj = Teacher('egon',18,'male',3000,10)# print(tea_obj.__dict__)# print(tea_obj.name)# tea_obj.score()# super()案例class A:    def test(self):        super().test()class B:    def test(self):        print('from B')class C(A,B):    passobj = C()obj.test()  # from Bprint(C.mro())# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]# 调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性# 先在C类里找,没有,然后去他爹那A类去找,没有,但是这里调用super()会得到一个特殊的对象,# 该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性,虽然肉眼看B不是A的爹,# 但是注意要看C.mro(),这里面B就是A的爹# 再次强调,super()是参照发起属性查找的那个mro,即C.mro(),而不是本身的A.mro
    
  • 多态

    # 1、什么是多态:同一事物有多种形态# class Animal:#     pass## class People(Animal):#     pass## class Pig(Animal):#     pass## class Dog(Animal):#     pass# 2、为何要有多态=》多态会带来什么样的特性,多态性#   多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象## class Animal:  # 统一所有子类的方法#     def say(self):#         print('动物的发声')## class People(Animal):#     def say(self):#         super().say()#         print('嘤嘤嘤')## class Pig(Animal):#     def say(self):#         super().say()#         print('汪汪汪')### class Dog(Animal):#     def say(self):#         super().say()#         print('哼哼哼')## obj1 = People()# obj2 = Dog()# obj3 = Pig()## # obj1.say()# # obj2.say()# # obj3.say()## # 定义统一的接口,接受传入的动物的对象# def animal_say(animal):#     animal.say()## animal_say(obj1)# animal_say(obj2)# animal_say(obj3)# len('hello')# len([1,2,3])# len({'a':1,'b':2})## print('hello'.__len__())# print([1,2,3].__len__())# print({'a':1,'b':2}.__len__())## # 多态性的提现# def my_len(val):#     return val.__len__()## print(my_len('hello'))# print(my_len([1,2,3]))# print(my_len({'a':1,'b':2}))# python 推崇的鸭子类型# 推崇不要继承父类,让这些毫无相关的类让他们做的像# class Cpu:#     def read(self):#         print('Cpu read')##     def write(self):#         print('Cpu write')## class Mem:#     def read(self):#         print('Mem read')##     def write(self):#         print('Mem write')## class Txt:#     def read(self):#         print('Txt read')##     def write(self):#         print('Txt write')## obj1 = Cpu()# obj2 = Mem()# obj3 = Txt()## obj1.read()# obj1.write()## obj2.read()# obj2.write()## obj3.read()# obj3.write()# 了解import abcclass Animal(metaclass=abc.ABCMeta):  # 将其做成一个抽象基类   # 统一所有子类的标准    def say(self):        print('动物的发声')# obj = Animal()  # 不能实例化抽象类自己class People(Animal):# 强制继承抽象基类的子类,都必须遵循父类的标准,都必须有其规定的方法,例如下面这些类都必须继承say功能,不然就会报错    def say(self):        super().say()        print('嘤嘤嘤')class Pig(Animal):    def say(self):        super().say()        print('汪汪汪')class Dog(Animal):    def say(self):        super().say()        print('哼哼哼')obj1 = People()obj2 = Dog()obj3 = Pig()# obj1.say()# obj2.say()# obj3.say()
    
posted @ 2021-07-22 15:26  ccFTD  阅读(36)  评论(0)    收藏  举报