zoukankan      html  css  js  c++  java
  • 面向对象之封装、继承、多态

    面向对象的三大特性:

    1. 封装:
      1. 在类的内部(class内部)可以由属性和方法,外部代码可以通过直接调用实例变量的方法来操作数据,这样就隐藏了内部的逻辑,但是外部还是可以直接修改实例的属性,因此当需求中存在需要内部属性不被外部访问,就可以把属性的名称前加__。
      2. 在Python中,实例的变量如果是以__开头的,就变成一个私有化属性,只有内部可以访问,外部不能访问。
    """
    什么是封装:
        装;网容器即名称空间里面存入名字
        封; 代表将存放与名称空间中的名字给藏起来,这种隐藏对外不对内
    
    为什么要封装:
        封数据属性:将数据属性隐藏起来,类外部无法直接操作属性,需要类内开辟一个接口来外部使用可以间接的操作属性,可以在接口内定义任意的控制逻辑,从而严格控制使用者对属性的操作
        封函数属性:隔离复杂度(开机只有一个按键实际上却又很多操作)
        
    如何封装:
        在类定义的属性前加__开头,(没有__结尾)
        
    底层原理:
        # __开头的属性实现隐藏,只是语法层面上的变形,并不会真的限制类外部的访问
        # 该变形操作只在类定义阶段检测语法时发生一次,类定义阶段之后新增的__开头的属性并不会变形
        # 如果父类不想让子类覆盖自己的属性,可以在属性前加__开头
    """
    class Student:
        
        def __init__(self, name, age, gender):
            self.name = name
            self.age = age
            self.gender = gender
        
        def study(self):
            print('学生%s,正在学习' % self.name)
    
    
    st1 = Student('qzk', 18, 'male')
    print(st1.__dict__)  # {'name': 'qzk', 'age': 18, 'gender': 'male'}
    
    
    class Student:
        
        def __init__(self, name, age, gender):
            self.name = name  # 姓名未隐藏封装
            self.__age = age  # 年龄设置为私有化属性
            self.__gender = gender  # 性别设置为私有化属性
        
        def __study(self):  # 类的函数属性也可以私有化
            print('学生%s,正在学习' % self.name)
        
        @property  # property 语法糖是将函数属性伪装成数据属性
        def age(self):  # 以函数伪装成数据的形式,通过 obj.age方式,去查看对象的私有化属性obj.__age
            return self.__age
        
        @age.setter  # 在property 装饰的函数下,内置了一个函数名.setter装饰器,用来修改私有化属性,作为修改私有化属性的接口
        def age(self, age):
            if not isinstance(age, int):
                raise TypeError('请输入正确的数据类型:int')
            else:
                self.__age = age
    
    
    st2 = Student('qzk', 18, 'male')
    print(st2.__dict__)  # {'name': 'qzk', '_Student__age': 18, '_Student__gender': 'male'}
    # 我们发现,被封装的属性,在名称空间的字典中存储的形式是'_类名__属性名',这在底层就是一种封装,属性私有化一种实现
    # 我们发现如果真的想访问该属性,可以使用如下方式:
    print(st2._Student__age)  # 18 该方式是直接访问底层的字典里面信息,一般不建议这样操作
    # print(st2.__age)  # AttributeError: 'Student' object has no attribute '__age',这样访问会报错,无法访问
    
    # 如果我们真的想访问该信息,一般我们会开一个借口并对其加以控制
    print(st2.age)  # 18
    st2.age = 19
    print(st2.age)  # 19
    st2.age = '十八'  # raise TypeError('请输入正确的数据类型:int')
    # TypeError: 请输入正确的数据类型:int

    # property 装饰器,是用来将类的函数属性伪装成数据属性,被装饰过后的函数属性,是不可以进行增删改查操作的,
    # 但是如果实际真的需要增删改查,则需要在所开接口上方进行装饰器(函数名.setter)操作
    
    

    二、继承:

    1. 分类: 继承分为单继承和多继承
    2. 定义: 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

    """
    继承的特点:
      1.在python中,一个子类可以同时继承多个父类
      2.在继承背景下去说,python中的类分为两种:新式类 和 经典类
        1.新式类: 但凡继承object的类,以及该类的子类,都称为新式类(python3中,所有继承关系,父类都默认继承object,因此都是新式类)
        2.经典类:经典类的概念只有在python2中存在,python2中类的继承,父类不默认继承object,如果是新式类,需要手动继承object。
        3.在新式类中属性的查找顺序(按照广度优先原则,最后查找object),在经典类中按照深度优先的原则。
        4.新式类与经典类的区别:继承中属性查找的实现原理不同
      3.继承: 利用继承来解决类与类之间的方法冗余问题
      4.派生:在子类派生的新方法中重用父类的功能和方法
    """
    #
    抽离:先有多个有共同点的类,抽离出共性形成的类 => 父类 # 派生:通过已有的父类,再去定义该类的子类,这种方式就叫做派生 # 继承:继承是一种关系,子类可以通过父类获取属性和方法,能获取的根据就是继承 # 继承的语法: # class 父类名:pass # class 子类名(父类名): pass class Sup: pass class Sub(Sup): pass # 继承的规则 # 1.父类的所有未封装的属性和方法,子类都能访问 # 2.父类的所有封装的属性和方法,子类都不能访问 # -- 在外界通过子类或子类对象,不能访问 # -- 在子类内部通过子类或子类对象也不能访问 class Sup: __num = 10 # 封装被更名为_Sup__num class Sub(Sup): def test(self): print(self.__num) # 本质去访问_Sub__num,所以不能访问 # 继承父类的方法:子类没有明文书写父类的方法,通过继承关系拿到 class Sup: def test(self): print(self) # 父类对象调用就是父类对象,子类对象调用就是当前调用的子类对象 class Sub(Sup): pass Sub().test() # 重写父类的方法:子类明文书写父类同名的方法,并且实现体自定义 class Sup: def test(self): print(self) # 父类对象调用就是父类对象,子类对象调用就是当前调用的子类对象 class Sub(Sup): def test(self): print('自己的方法', self) Sub().test() # 重用父类的方法:子类明文书写父类同名的方法,有自己的实现体,但也用父类原有的功能 class Sup: def test(self): print(self) # 父类对象调用就是父类对象,子类对象调用就是当前调用的子类对象 class Sub(Sup): def test(self): super().test() # 本质 super(Sub, self).test() py2必须这么写 print('自己的方法', self) Sub().test()

        super()方法

    class Sup:
        def __init__(self, name):
            self.name = name
        
        def test(self):
            print(self)
    
    
    class Sub(Sup):
        # 默认父级的__init__可以被继承过来,
        # 但是会出现子类对象的属性比父类多
        def __init__(self, name, salary):
            super().__init__(name)  # 父级有的共性功能通过super()交给父级做
            self.salary = salary  # 子类特有的自己来完成
        
        # 有继承关系下,只要名字相同,即使参数不同,还是属于同一个方法
        def test(self, num):
            super().test()  # 使用父级的方法
            print(num)
            
    # 外界通过Sub对象来调用test方法,一定找自己的test方法(属性的查找顺序)
        
    
    # 重点:super() 可以得到调用父级功能的对象,调用者还是子类对象
    #         -- super()只能在子类的方法中使用
    #        -- super()本质 super(子类类名, 当前对象)
    #        -- super().父类普通方法 | super().__init__() | super()能调用父类所有可继承方法
    '''
    继承
    
    1.父类:在类后()中写父类们
    class A:pass
    class B:pass
    class C(A, B):pass
    
    2.属性查找顺序:自己 -> ()左侧的父类 -> 依次往右类推
    
    3.抽离:先定义子类,由子类的共性抽离出父类 - 派生:父类已经创建,通过父类再去派生子类
    
    4.继承关系
        -- 1)父类的所有非封装的属性和方法均能被继承
        -- 2)父类的所有封装的属性和方法均能被继承
        -- 3)在子类中要去使用父类的方法
            -- 子类继承父类方法:子类不需要去实现父类的方法,子类对象可以直接调用父类方法
            -- 重写父类的方法:方法名与父类相同,实现体与父类不同,子类对象调用的是自身方法
            -- 重用父类的方法:方法名与父类相同,实现体中有自己的逻辑也调用了父类的方法(super())
                -- super():在子类中获取可以调用父类方法的对象,且在父类中体现的调用者子类或子类对象
            
    5.复杂继承:一个类可以继承多个类,查找顺序是根据继承父类从左往右的顺序,并且在查找第一个父类时,将父类的父类也进行查找(一个父类分支全部查找完毕才去查找下一个父类分支)    
    
    6.棱形继承
        -- 经典类:py2中类不默认继承object,所以没有明确继承的类就没有继承任何类,这样的类称之为经典类
        -- 新式类:所有直接或间接继承object的类,py2中主动继承object的类及py3中所有的类
        
        -- 前提:父类中有共有属性或方法,子类没有自己去定义这些属性和方法,必须从父类中获取,到底从哪个父类中获取
        -- 经典类:深度查找 a -> b -> d -> c
        -- 新式类:广度优先 a -> b -> c -> d
        d
    b        c
        a
    '''

    super().属性    方法与    类名.属性    方法    调用父类属性在继承上的区别

    """
    在子类派生出啦的新方法中重用父类功能2:super()必须在类中用
    super(自己的类名,自己的对象) 必须在类中用
    在python3中,
    super()
        调用该函数会得到一个返回值,是一个特殊的对象,该对象专门用来访问父类的属性!!!完全按照mro列表
        
        总结:严格依赖继承的mro表
        访问是绑定方式,有自动传值的效果
       
    """
    1. 多态:
      # 多态:对象的多种状态 - 父类对象的多种(子类对象)状态
      
      import abc
      class People(metaclass=abc.ABCMeta):
          def __init__(self, name):
              self.name = name
          @abc.abstractmethod
          def speak(self): pass
      
      class Chinese(People):
          def speak(self):
              print('说中国话')
      class England(People):
          def speak(self):
              print('说英国话')
              
      if __name__ == '__main__':
          # 多态的体现:功能或是需求,需要父类的对象,可以传入父类对象或任意子类对象
          #       注:一般都是规定需要父类对象,传入子类对象
          def ask_someone(obj):
              print('让%s上台演讲' % obj.name)  # 父类提供,自己直接继承
              obj.speak()  # 父类提供,只不过子类重写了
      
          ch = Chinese('王大锤')
          en = England('Tom')
          
          # 传入Chinese | England均可以,因为都是People的一种状态(体现方式)
          ask_someone(ch)
          ask_someone(en)
          
          # 传入str不可以,因为str的对象没有name和speak
          # s = str('白骨精')
          # ask_someone(s)
          # p = People('kkk')

      鸭子类型:

    2. # 需求:需要一个对象,该对象有name属性及speak方法,就可以作为一种状态的体现被传入
      def ask_someone(obj):
          print('让%s上台演讲' % obj.name)
          obj.speak()
      
      # 鸭子类型:
      # 1.先规定:有什么属性及什么方法的类的类型叫鸭子类型
      # 2.这些类实例化出的对象,都称之为鸭子,都可以作为需求对象的一种具体体现
      class A:
             # 能有自己特有的属性和方法,可以和B完全不一样,但是必须有鸭子类型规定的属性和方法,不然就不是鸭子类型
          def __init__(self, name):
              self.name = name
          def speak(self): 
              print('说AAAA')
         
              
      class B:
          # 能有自己特有的属性和方法,可以和A完全不一样,但是必须有鸭子类型规定的属性和方法,不然就不是鸭子类型
          def __init__(self, name):
              self.name = name
          def speak(self): 
              print('说BBBB')
              
      ask_someone(B('B'))
  • 相关阅读:
    泰山之行
    泰山之行
    Java EE (2) -- Java EE 6 Enterprise JavaBeans Developer Certified Expert(1z0-895)
    一、浏览器生成消息(2)
    P1194 买礼物 洛谷
    P1195 口袋的天空 洛谷
    P1546||2627 最短网络 Agri-Net 洛谷||codevs
    P3366 最小生成树【模板】 洛谷
    T2627 村村通 codevs
    【目录】我的原创技术视频教程
  • 原文地址:https://www.cnblogs.com/qianzhengkai/p/10750877.html
Copyright © 2011-2022 走看看