• OOP的三大特征之继承 | 了解组合与继承的原理,以及菱形继承与mro列表


    今日内容:


     

    OOP的三大特征之一:


     

    封装,继承,多态

    继承:


     

    什么是继承?

    • 继承是一种关系,是描述两个对象之间,什么是什么的关系
    • 在程序中,继承描述的是类和类之间的关系

    例如:

    • a继承了b,a就能直接使用b已有的方法和属性;
    • a称之为子类,b称之为父类或者基类

    为什么要使用继承?

    • 继承的一方可以直接使用被继承一方的已有的东西
    • 其目的是为了重用已经有的代码,提高重用性

    如何使用继承?

    继承的基本语法:

    class 类名称(父类的名称):
    # 在python中,一个子类可以同时继承多个父类
    class Base:
        desc = '这是一个基类'
        
        def show_info(self):
            print(self.desc)
            
        def make_money(self):
            print('一天赚一个亿...')
            
            
    # 如果不继承Base类        
    class SubClass: 
        def make_money(self):
            print('一天赚一百...')     
    obj = SubClass()
    obj.make_money()  # 一天赚一百
    ​
    ​
    # 如果继承了Base类     
    class SubClass(Base):
        def make_money(self):
            print('一天赚一百...')  
    obj = SubClass()
    obj.make_money()  # 一天赚一个亿
    print(obj.desc)  # 这是一个基类
    ​
    ​
    # 指定父类Base:
    class Subclass()
        pass
    obj = SubClass()
    # 即使类中什么也没有,也可以使用类中的方法
    obj.make_money()  
    print(obj.desc)  

    抽象:

    表现形式:不具体,不清晰,很模糊,看不懂

    定义:

    • 将多个子类中相同的部分进行抽取,形成一个新的类,这个过程就是抽象的过程

    正确的使用继承:

    • 1.先抽象,再继承
    • 2.继承一个已经现有的类,扩展或是修改原始的功能

    继承与抽象:

    class Teacher:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
            
        def say_hi(self):
            print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age))
         
        def teaching(self):
            print('老师教学生,写代码')
            
    t1 = Teacher("jack","male",20)
    t1.say_hi()
    ​
    ​
    # 如果不继承Teacher,有冗余代码
    class Student:
        def __init__(self,name,gender,age,number):
            self.name = name
            self.age = age
            self.gender = gender
            self.numuber = number
            
        def say_hi(self):
            print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age))
        
    stu1 = Student("rose","female",18,"xxx01")
    stu1.say_hi()
    ​
    ​
    # 如果继承Teacher
    class Student(Teacher):
        pass
    ​
    stu1 = Student("rose","female",18)
    stu1.say_hi()
    ​
    ​
    # 开始抽象
    # 抽取老师和学生中相同的类
    class Person:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
            
        def say_hi(self):
            print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age))
            
    class Teacher(Person):
        def teaching(self):
            print('老师教学生,写代码')
            
    t1 = Teacher("jack","male",20)
    t1.say_hi()
            
    class Student(Person):
        pass
    ​
    stu1 = Student("rose","female",18)
    stu1.say_hi()

    属性查找顺序:

    先找对象本身的>>>所在类中>>>找父类>>>父类的父类>>>object

    # 正常继承
    class A:
        text = 'hahaha' 
    class B(A):
        text = "heihei" 
    b = B()
    print(b.text)  # heihei
    ​
    ​
    # 继承后,B类中pass
    class A:
        text = 'hahaha'
    class B(A):
        pass
    b = B()
    print(b.text)  # hahaha
    ​
    ​
    # 继承后,b给本身赋了值
    class A:
        text = 'hahaha'
    class B(A):
        text = "heihei"
    b = B()
    b.text = "xixi"
    print(b.text)  # xixi

    派生:

    定义:

    • 当一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类
    • 通常子类都会写一些新的代码,不可能与父类完全一样,即都是派生类

    总结:派生类指的就是子类

    覆盖:

    定义:

    • 它也称之为重写overrides
    # 当子类出现了与父类名称完全一致的属性或方法
    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' 因为hello被覆盖了

    练习:实现一个可以限制元素类型的容器(列表,字典,元祖,集合,字符串)

    涉及知识点:我们需要访问父类的append函数来完成真正的存储操作

    class MyList(list):
        def __init__(self, element_type):
            # 初始化
            super().__init__(element_type)
            self.element_type = element_type
            
        def append(self, object):
            if type(object) == self.element_type:
                # 这里我们需要访问父类的append函数来完成真正的存储操作
                super(MyList,self).append(object)
            else:
                print('你的element_type not is %s' % self.element_type)
    ​
    
    # 创建时指定要存储的元素类型
    m = MyList(int)
    # 当你有需求,是需要在创建对象时,干点什么事儿,那就应该在初始化方法时操作
    m.append(l)
    print(m[0])
    m.append('121212')   

    子类中访问父类的内容:


     

    语法:

    # 方式1:
    super(当前类名称,self).你要调用的父类的属性或方法
    
    # 方式2:
    super().你要调用的父类的属性或者方法
    
    # 方式3:其实这种方式与继承无关
    类名称.你要调用的父类的属性或者方法

    注意点:

    • 当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需要的参数

    实例:

    class Parent:
        text = 'abc'
        def say_something(self):
            print('anything')
            
    class Sub(Parent):
        def show_info(self):
            # 访问方式1
            print(super(Sub,self).text)
            super(Sub,self).say_something()
            
            # 最常用的!访问方式2 py3中的新语法(推荐使用)
            print(super().text)
            super().say_something()
            
            # 访问方式3 直接指定类型调用
            print(Parent.text)
            Parent.say_something(self)
            
    sub = Sub()
    sub.show_info()

    初始化方法中必须调用super.py:

    class Person:
        def __init__(self,name,gender,age):
            self.name = name
            self.gender = gender
            self.age = age
        def say_hi(self):
            print('name:%s, gender:%s, age:%s' % (self.name, self.gender, self.age))
    
    class Student(Person): def __init__(self,name,gender,age,number): super().__init__(name,gender,age) self.number = number def say_hi(self): super().say_hi() print('number:%s' % self.number)
    stu
    = Student('rose', 'male', 20, '0001') stu.say_hi()

    组合:


     

    定义:

    • 它也是一种关系,描述两个对象之间,是什么有什么的关系
    • 将一个对象作为另一个对象的属性(即什么有什么)

    组合的目的:

    • 它也是为了重用现有代码

    什么时候使用继承:

    • 分析两个类的关系,到底是不是----什么是什么的关系

    什么时候使用组合:

    • 如果两个类之间,没有什么太大的关系,完全不属于同类

    补充知识点:

    • 组合相比于继承,耦合度更低

    例如:学生有手机,游戏中角色拥有的某些装备

    class Phone:
        def __init__(self,price,kind,color):
            self.price = price
            self.kind = kind
            self.color = color
        def call(self):
            print('正在呼叫XXX:')
        def send_msg(self):
            print('正在发送短信:')
    
     
    class Student:
        def __init__(self,name,gender,phone):
            self.name = name
            self.gender = gender
            self.phone = phone
        def show_info(self):
            print('name:%s, gender:%s' % (self.name, self.gender))
    
    
    phone = Phone(1000,'apple','red')
    stu1 = Student('rose','male',phone)
    stu1.phone.call()  # 正在呼叫XXX:

    了解知识点:继承的原理


     

    菱形继承与mro列表:

    首先要了解python支持多继承

    # 讨论一些python支持多继承可能会出现的问题
    class A:
        pass
    class B:
        pass
    class B:
        passclass Test(A,B,C):
        pass
    print(Test.mro())  
    # [ <class '__main__.Test'>,<class '__main__.A'>,
    <class '__main__.B'>,<class '__main__.C'>,object ]

    补充: 新式类与经典类

    • python3中任何类都是直接或间接的继承了Object
    • 新式类,任何显式或者隐式地继承自object的类就称之为新式类,python3中全都是新式类
    • 经典类,既不是object的子类,仅仅在python2中出现

    新式类与经典类在菱形继承中的体现:

    class A:
        j = 1
        pass
    class B(A):
        j = 2
        pass
    class C(A):
        j = 3
        pass
    class D(A):
        j = 4
        pass
    ​
    d = D()
    print(d,j)  # 输出4,如果D中j = 2注掉,则输出3;如果C中j = 2注掉,则输出2,依次往上输入

    总结:

    当出现了菱形继承时:

    • 新式类,先深度,当遇到了共同父类时就广度
    • 经典类,单纯的深度优先
  • 相关阅读:
    Spring Boot 配置元数据指南
    面试中常被提到的最左前缀匹配原则
    MyBatis缓存机制(一级缓存,二级缓存)
    计算机网络基础知识
    垃圾收集算法与垃圾收集器
    递归与分治策略
    五种IO模型和BIO,NIO,AIO
    七种阻塞队列
    ConcurrentHashMap(1.7版本和1.8版本)
    重入锁 ReentrantLock
  • 原文地址:https://www.cnblogs.com/zhukaijian/p/11246947.html
Copyright © 2020-2023  润新知