• day 25-1 接口类、抽象类、多态


    # 接口类:python 原生不支持
    # 抽象类:python 原生支持的

    接口类

    首先我们来看一个支付接口的简单例子

    from abc import abstractmethod,ABCMeta #我们也可以创建一个规范类
    class Payment(metaclass=ABCMeta):  # 元类 默认的元类 type
        @abstractmethod
        def pay(self,money):
            raise Notlmplemented
    
    #规范类要求需要实现一个 叫做 pay 的方法
    #这样在不调用的情况下 就会抛出错误  在实例化时就可以发现这个问题
    
    # 规范 :接口类或者抽象类都可以
    # 接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java
    # 抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java
    
    class Payment:
        def pay(self,money):  #2.没有实现该方法,就会抛出这个异常,用来隐藏 1 处的错误
            raise Notlmplemented  #原理就是 如果不实现 pay 方法 就会抛出这个报错
    #报错如下
    #NameError: name 'Notlmplemented' is not defined
    
    class Weipay(Payment):
        def pay(self,money):
            print('微信支付了%s元'%money)
    
    class Alipay(Payment):
        def pay(self,money):
            print('支付宝支付了%s元'%money)
    
    class Apppay(Payment):
        def pays(self,money):  #1.如果这里的 pay 换成了其他名字就会报错  与下面的 pay 函数冲突了
            print('苹果支付了%s元'%money)
    wei = Weipay()
    ali = Alipay()
    app = Apppay()
    
    def pay(pay_obj,money):
        pay_obj.pay(money)
    
    pay(wei,100)
    pay(ali,100)
    pay(app,100)
    #weipay.pay(100)
    #alipay.pay(100)

    接口类的多继承

    这是三种动物
    tiger      走路  游泳
    swan     走路  游泳 飞
    oldying  走路  飞

    为了避免代码重复,我们写以下三个类
    下面就是实现了 接口类的规范 不需要有功能实现的代码

    #下面就是实现了 接口类的规范 不需要有功能实现的代码
    from abc import abstractmethod,ABCMeta
    class Swim_Animal(metaclass=ABCMeta):
        @abstractmethod
        def swim(self):pass
    
    class Walk_Animal(metaclass=ABCMeta):
        @abstractmethod
        def walk(self):pass
    
    class Fly_Animal(metaclass=ABCMeta):
        @abstractmethod
        def fly(self):pass
    
    #继承了类里面的相应的方法就必须要写
    class Tiger(Walk_Animal,Swim_Animal):
        def walk(self):
            pass
        def swim(self):
            pass
    class OldYing(Fly_Animal,Walk_Animal):pass
    class Swan(Swim_Animal,Walk_Animal,Fly_Animal):pass
    
    # 这样就完了 接口类  刚好满足接口隔离原则 面向对象开发的思想 规范

    python 中没有接口类 这个只是类似,就是说 我要实现不同的功能,我就去继承不同的接口,来规范我当前类中要用那些函数

    好了我们接下来稍稍完善下上满的例子

    from abc import abstractmethod,ABCMeta
    class Swim_Animal(metaclass=ABCMeta):
        @abstractmethod
        def swim(self):pass
    
    class Walk_Animal(metaclass=ABCMeta):
        @abstractmethod
        def walk(self):pass
    
    class Fly_Animal(metaclass=ABCMeta):
        @abstractmethod
        def fly(self):pass
    
    class Tiger(Walk_Animal,Swim_Animal):
        def walk(self,num):
            print('狮子跑了 %s 公里'%num)
        def swim(self,num):
            print('狮子游了 %s 公里'%num)
    
    class OldYing(Fly_Animal,Walk_Animal):
        def fly(self,num):
            print('老鹰飞了 %s 公里'%num)
        def walk(self,num):
            print('老鹰跑了 %s 公里'%num)
    
    
    class Swan(Swim_Animal,Walk_Animal,Fly_Animal):
        def fly(self,num):
            print('天鹅飞了 %s 公里'%num)
        def walk(self,num):
            print('天鹅跑了 %s 公里'%num)
        def swim(self,num):
            print('天鹅游了 %s 公里'%num)
    
    T = Tiger()
    o = OldYing()
    s = Swan()
    #s.fly(5)
    
    def action(action_obj,num):
        action_obj(num)
    
    action(s.fly,5)

    抽象类

    如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性
    在 python 中实现抽象类,抽象类一般是单继承
    操作系统一切皆文件

    我们来实现一个抽象类

    import abc #利用 abc 模块实现抽象类
    
    class All_file(metaclass=abc.ABCMeta):
        all_type='file'
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def read(self):
            '子类必须定义读功能'
            with open('filaname') as f:
                pass
    
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def write(self):
            '子类必须定义写功能'
            pass
    
    class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('文本数据的读取方法')
        def write(self):
            print('文本数据的读取方法')
    
    class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的读取方法')
    
    class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('进程数据的读取方法')
    
        def write(self):
            print('进程数据的读取方法')
    
    wenbenwenjian=Txt()
    
    yingpanwenjian=Sata()
    
    jinchengwenjian=Process()
    
    #这样大家都是被归一化了,也就是一切皆文件的思想
    wenbenwenjian.read()
    yingpanwenjian.write()
    jinchengwenjian.read()
    
    print(wenbenwenjian.all_type)
    print(yingpanwenjian.all_type)
    print(jinchengwenjian.all_type)

    抽象类 : 规范
    一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
    多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中

    抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化

    java:
    java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
    但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念
    来解决多继承的规范问题

    python:
    python中没有接口类 :
    python中自带多继承 所以我们直接用class来实现了接口类
    python中支持抽象类 : 一般情况下 单继承 不能实例化(接口类抽象类)
    且可以实现python代码

    多态

    python 天生支持多态,python 是动态强类型的语言

    多态指的是一类事物有多种形态
    动物有多种形态:人,狗,猪

    import abc
    class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    class People(Animal): #动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal): #动物的形态之二:狗
        def talk(self):
            print('say wangwang')
    
    class Pig(Animal): #动物的形态之三:猪
        def talk(self):
            print('say aoao')
    
    People().talk()

    文件有多种形态:文本文件,可执行文件

    多态性
    什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)
    多态性是指在不考虑实例类型的情况下使用实例

    在面向对象方法中一般是这样表述多态性:
    向不同的对象发送同一条消息( !!!obj.func():是调用了obj的方法func,又称为向 obj 发送了一条消息 func ),不同的对象在接收时会产生不同的行为(即方法)。
    
    也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
    
    比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

    鸭子类型

    # 鸭子类型
    # list tuple 如果说两个类型为鸭子类型 就是说这两个类型很相识
    # 不崇尚根据继承所得来的相似
    # 我只是自己实现我自己的代码就可以了。
    # 如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型
    # list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的
    # 优点:松耦合 每个相似的类之间都没有影响
    # 缺点:太随意了,只能靠自觉

    # 接口类和抽象类 在 python 当中的应用点并不是非常必要

    封装

    # 广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种
    # 只让自己的对象能调用自己类中的方法

    # 狭义上的封装 —— 面向对象的三大特性之一
    # 属性 和 方法都藏起来 不让你看见

    class Person:
        __key = 123  # 私有静态属性
        def __init__(self,name,passwd):
            self.name = name
            #self.passwd = passwd  
            self.__passwd = passwd  #当属性前面加上 __ 就成为了私有属性
    
        def get_pwd(self):          #在类的内部使用方法调用 __passwd 属性
            print(self.__dict__)
            return self.__passwd    #只要在类的内部使用私有属性,就会自动的带上_类名
    
        def __login(self):          #私有方法
            self.__get.pwd()
    
    per = Person('ysg','123')
    #print(alex.passwd)
    #print(per.__passwd)  #然后就找不到该属性了
    print(per.__dict__)   #这里还是可以看到  所有私有属性并不是约束数据安全的只是在代码级别加了一层密
    print(per._Person__passwd)  # _类名__属性名  这样调用就可以了 不过不应该去调取
    print(per.get_pwd())
    
    print(per.__login)   #调取不到
    #定义类的私有属性 只能在类的内部 外部就不可以
    per.__high = 1
    print(per.__high)  #外部就可以直接使用

    所有的私有 都是在变量的左边加上双下划线
      对象的私有属性
      类中的私有方法
      类中的静态私有属性
    所有的私有的 都不能在类的外部使用

    封装之隐藏

    如何隐藏:在 python 中用双下划线开头的方式将属性隐藏起来(设置成私有的)

    class A:
        __x = 1
    
        def __init__(self, name):
            self.name = name
    
        def __foo(self):
            print('this is foo')
    
    
    #print(A.__x)   
    # AttributeError: type object 'A' has no attribute '__x'
    a = A('ysg')
    print(a.name)   # ysg
    #print(A.__foo)  #AttributeError: type object 'A' has no attribute '__foo'
    
    # 我们来查看下 class A 的属性
    # 可以看到并没有 __x、__foo 的属性,但有了 _A__x、_A__foo 的属性
    print(A.__dict__) #{'__module__': '__main__', '_A__x': 1, '__init__': <function A.__init__ at 0x000001D63A0602F0>, '_A__foo': <function A.__foo at 0x000001D63A0BB268>,,...}

    如上,我们可以大胆的猜测一下,当我们使用双下划线隐藏后,它可能做了某种变形,我们来尝试下

    print(A._A__x)      # 1
    print(A._A__foo)    # <function A.__foo at 0x000001D26E09D1E0>

    可以看出,的确是做了变形的处理

    接下来看下它有哪些特点:

    1. 类中定义的 __x 只能在内部使用,如 self.__x,引用的就是变形的结果
    2. 这种变形其实正是针对外部的变形,在外部无法通过 __x 这个名字访问到
    3. 在子类定义的 __x 不会覆盖在父类定义的 __x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的

    这种变形需要注意的问题是:

    1. 这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
    2. 变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
    3. 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

    我们用一个例子来验证下

    class B:
        def __foo(self):
            print('B.foo')
    
        def bar(self):
            print('B.bar')
            self.__foo()
    
    
    class C(B):
        def __foo(self):
            print('C.foo')
    
    
    c = C()
    c.bar()
    
    
    # 结果:
    B.bar
    B.foo

    封装不是单纯意义的隐藏

    1.封装数据属性:明确区分内外,控制外部对隐藏属性的操作

    将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制

    我们来写一个例子

    class People:
        def __init__(self, name, age):
            self.__name = name
            self.__age = age
    
        def tell_info(self):
            print('Name:<%s>
    Age:<%s>' % (self.__name, self.__age))
    
        def set_info(self, name, age):
            if not isinstance(name, str):
                print('名字必须是字符串类型')
                return
            if not isinstance(age, int):
                print('年龄必须是 int 类型')
                return
            self.__name = name
            self.__age = age
    
    
    p = People('ysg', 16)   
    p.tell_info()
    # Name:<ysg>
    # Age:<16>
    
    p.set_info(123, 18)     # 名字必须是字符串类型
    p.tell_info()
    p.set_info('er', '18')  # 年龄必须是 int 类型
    p.tell_info()
    # Name:<ysg>
    # Age:<16>
    
    
    p.set_info('er', 18)
    p.tell_info()
    # Name:<er>
    # Age:<18>

    上面可以看到:

    tell_inso() 实现了在不能直接查看属性时,开一个接口且按照一定的规格进行查看属性

    set_info() 实现了在不能直接修改属性时,开一个接口且对传来的属性进行判断,通过则进行修改

    2.封装方法:隔离复杂度

    我们来看下面的例子

    1.取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    2.对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    3.隔离了复杂度,同时也提升了安全性
    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()
    
    
    a = ATM()
    a.withdraw()

    注意:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体

    封装与可扩展性

    封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑

    class Room:
        def __init__(self,name,owner,weight,length,height):
            self.name=name
            self.owner=owner
            
            # 这里访问者其实是要结果,值可以隐藏起来
            self.__weight=weight
            self.__length=length
            self.__height=height
        
        #在这里改变运算规则
        def tell_area(self):
            return self.__weight * self.__length * self.__height
    
    r=Room('卫生间','alex',10,10,10)
    
    # print(r.tell_area())
    
    #用户调用方式不变
    print(r.tell_area())
        

    达到了:对外提供的接口中,隐藏了内部的实现细节;扩展了功能,但不需要改变调用的方法

  • 相关阅读:
    The executable was signed with invalid entitlements
    iOS7 文本转语音 AVSpeechSynthesizer
    The document "ViewController.xib" could not be opened. Could not read archive.
    UIPanGestureRecognizer
    UINavigationController
    IE6下面的css调试工具
    15款最好的网站音乐播放器
    ASP.NET常用加密解密方法
    ASP.NET根据IP获取省市地址
    强悍的CSS工具组合:Blueprint, Sass, Compass
  • 原文地址:https://www.cnblogs.com/ysging/p/10293879.html
Copyright © 2020-2023  润新知