• 二十二、封装,多态,鸭子类型,__setattr__,__delattr__,__getattr__


    一、封装 :

    ​ 什么是封装,就是将复杂的丑陋的,隐私(密码。身份证号码)的细节隐藏到内部,对外提供简单的使用接口

    ​ 对外隐藏内部实现细节,并提供访问的接口

    ## 为什么需要封装 :

    ​ 两个目的

    ​ 1.为了保证 关键数据的安全性

    ​ 2.对外部隐藏实现细节,隔离复杂度

    ## 封装的应用场景

    ​ 当有一些数据不希望外界可以直接修改时

    ​ 当有一些函数不希望给外界使用时,

    好处:

    ​ 1.提高安全性

    ​ 2.隔离复杂度

    二、封装的基础语法:

    语法:

    ```python
    class Person:
    def __init__(self,id_number,name,age)
    self.__id_number = id_number
    self.name = name
    self.age = age

    p = Person("1111111111111","jack",29)

    p.id_number = "222"

    """
    这是一个下载器类,需要提供一个缓存大小这样的属性
    缓存大小不能超过内存限制
    
    """
    
    
    class Downloader:
        def __init__(self, filename, url, buffer_size):
            self.filename = filename
            self.url = url
            self.__buffer_size = buffer_size
    
        def start_download(self):
            if self.__buffer_size <= 1024 * 1024:
                print("开始下载....")
                print("当前缓冲器大小", self.__buffer_size)
            else:
                print("内存炸了! ")
    
        def set_buffer_size(self, size):
            # 可以在方法中添加额外的逻辑
            if not type(size) == int:
                print("大哥 缓冲区大小必须是整型")
            else:
                print("缓冲区大小修改成功!")
                self.__buffer_size = size
    
        def get_buffer_size(self):
            return self.__buffer_size
    
    
    d = Downloader("葫芦娃", "http://www.baicu.com", 1024 * 1024)
    
    # 通过函数取修改内部封装的属性
    d.set_buffer_size(1024 * 512)
    
    # 通过函数访问内部封装的属性
    print(d.get_buffer_size())
    print(d.filename)
    
    d.start_download()
    View Code

    ##### 被封装的内容的特点:

    ​ 1.外界不能直接访问

    ​ 2.内部依然可以使用

    ## 权限

    学习了封装后就可以控制属性的权限

    在python只要两种权限,

    1.公开的.默认就是公开的

    2.私有的,只能由当前类自己使用

    三、property装饰器

    通过方法来修改或访问属性,本身没什么问题,但是这给对象的使用者带来了麻烦.

    使用必须知道哪些是普通属性,哪些是私有属性,需要使用不同的方式来调用他们

    property装饰就是为了使得调用方式一致

    有三个相关的装饰器

    ```python
    1.property 该装器用在获取属性的方法上
    2.@key.setter 该装器用在修改属性的方法上
    3.@key.deleter 该装器用在删除属性的方法上


    注意:key是被property装饰的方法的名称 也就是属性的名称
    内部会创建一个对象 变量名称就是函数名称
    所以在使用setter和deleter时 必须保证使用对象的名称取调用方法
    所以是 key.setter
    ```

    案例:

    ```python
    class A:
    def __init__(self,name,key):
    self.__name = name
    self.__key = key

    @property
    def key(self):
    return self.__key

    @key.setter
    def key(self,new_key):
    if new_key <= 100:
    self.__key = new_key
    else:
    print("key 必须小于等于100")


    @key.deleter
    def key(self):
    print("不允许删除该属性")
    del self.__key

    a = A("jack",123)
    print(a.key)
    a.key = 321
    print(a.key)
    ```

    class A:
        def __init__(self, name, key):
            self.name = name
            self.__key = key
    
        def get_key(self):
            return self.__key
    
        def set_key(self, new_key):
            self.__key = new_key
    
        # 加装饰器可以快速访问,私有属性
        @property
        def key(self):
            return self.__key
    
        # 可以直接修改
        @key.setter
        def key(self, new_key):
            self.__key = new_key
    
        @key.deleter
        def key(self):
            print("不允许删除该属性")
            del self.__key
    
    
    a = A("tangk", 123)
    print(a.name)
    a.set_key(321)
    del a.key
    print(a.get_key())
    print(a.key)
    property 可以用来实现计算属性
    
    计算属性指的是:属性的值,不能直接获得,必须通过计算才能获取
    
    例如:正方形求面积
    class Square:
    
        def __init__(self, width):
            self.width = width
            # self.area = self.width * self.width
    
        @property
        def area(self):
            return self.width * self.width
    
    
    # s = Square(10)
    # print(s.area())
    
    # 如果重新赋值,初始化就没开始,所以只能用方法
    # print(s.area)
    #
    # s.width = 20
    
    # print(s.area)
    #
    # s.width = 2
    # print(s.area)
    练习.用property以用来实现计算属性

    四、 python实现封装的原理

    就是在加载类的时候,把\_\_替换成了 \_类名\__

    python一般不会强制要求程序必须怎么怎么的,

    class A:
        def __init__(self,key):
            self.__key = key  # 在封装的时候把属性名改为_A__key
    
        @property
        def key(self):
            return self.__key
    
        @key.deleter
        def key(self):
            del self.__key
    
    
    a = A("123")
    # print(a.key)
    # print(a.__dict__)
    
    print(a._A__key)
    
    a.__name = 1
    
    print(a.__dict__)
    
    
    # print("__key".replace("__","_A__"))
    View Code 

    五、接口 

    接口是一组功能的集合,但是接口中仅包含功能的名字,不包含具体的实现代码

    接口本质是一套协议标准,遵循这个标准的对象就能被调用

      接口类:默认多继承,接口类的所有的方法都必须不能实现-----从java演化而来
    # 抽象类:不支持多继承,抽象类可以中方法可以有一些代码的实现

    接口目的就是为了提高扩展性建立规范:

    案例:

    class Mouse:
        def open(self):
            print("鼠标开机.....")
    
        def close(self):
            print("鼠标关机了...")
    
        def read(self):
            print("获取了光标位置....")
    
        def write(self):
            print("鼠标不支持写入....")
    
    
    def pc(usb_device):
        usb_device.open()
        usb_device.read()
        usb_device.write()
        usb_device.close()
    
    
    m = Mouse()
    # 将鼠标传给电脑
    pc(m)
    
    
    class KeyBoard:
        def open(self):
            print("键盘开机.....")
    
        def close(self):
            print("键盘关机了...")
    
        def read(self):
            print("获取了按键字符....")
    
        def write(self):
            print("可以写入灯光颜色....")
    
    
    # 来了一个键盘对象
    k = KeyBoard()
    pc(k)
    
    
    class UDisk:
    
        def open(self):
            print("U盘启动了...")
    
        def close(self):
            print("U盘关闭了...")
    
        def read(self):
            print("读出数据")
    
        def write(self):
            print("写入数据")
    
    
    u = UDisk()
    
    pc(u)
    View Code

    六、 抽象类

    指的是包含抽象方法(没有函数体的方法)的类,

    作用:可以限制子类必须类中定义的抽象方法

    最后:python一般不会限制你必须怎么写,作为一个优秀的程序员,就应该自觉遵守相关协议

    所以有了鸭子类型这么一说:

    如果这个对象长得像鸭子,走路像鸭子,那就他是鸭子

    你只要保证你的类按照相关的协议类编写,也可以达到提高扩展性的目的

    接口是一套协议规范,明确子类们应该具备哪些功能

    抽象类是用于强制要求子类必须按照协议中规定的来实现

    然而,python不推崇限制你的语法, 我们可以设计成鸭子类型,既让多个不同类对象具备相同的属性和方法

    对于使用者而言,就可以以不变应万变,轻松的使用各种对象

     七、多态

    """多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
    定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的结果
    好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化
    实现步骤:
    1.定义父类,并提供公有方法
    2.定义子类,并重写父类方法
    3.传递子类对象给调用者,可以看到不同子类执行效果不同
    """
    # 需求:警务人员和警犬一起工作,警犬分为两种:追击敌人和追查毒品,携带不同的警犬,执行不同的任务
    # 1. 定义父类
    
    
    
    class Dog(object):
    
        def work(self):
            pass
    
    
    # 2.定义子类,子类重写父类方法:定义两个类表示不同的警犬
    class Armydog(Dog):
        def work(self):
            print("追击敌人")
    
    
    class Drugdog(Dog):
        def work(self):
            print("追查毒品")
    
    
    # 定义人类
    class Person:
        def work_with_dog(self, dog):
            dog.work()
    
    
    # 3.创建对象,调用不同功能,观察不同结果
    
    ad = Armydog()
    du = Drugdog()
    p = Person()
    p.work_with_dog(ad)

     八、abc模块的使用

    """
    abc 不是随意取的 而是单词的缩写
    abstract class
    翻译为抽象类
    抽象类的定义 :
    类中包含 没有函数体的方法
    
    
    """
    import abc
    
    class AClass(metaclass=abc.ABCMeta):
    
        @abc.abstractmethod
        def run(self):
            pass
        @abc.abstractmethod
        def run(self):
            pass
    
    
    class B(AClass):
    
        def run(self):
            print("runrunrurn...")
    
    b = B()
    # 用abc模块更好的规范,代码

    __setattr__,__delattr__,__getattr__

    class Foo:
        x=1
        def __init__(self,y):
            self.y=y
    
        def __getattr__(self, item):
            print('----> from getattr:你找的属性不存在')
    
    
        def __setattr__(self, key, value):
            print('----> from setattr')
            # self.key=value #这就无限递归了,你好好想想
            # self.__dict__[key]=value #应该使用它
    
        def __delattr__(self, item):
            print('----> from delattr')
            # del self.item #无限递归了
            self.__dict__.pop(item)
    
    #__setattr__添加/修改属性会触发它的执行
    f1=Foo(10)
    print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
    f1.z=3
    print(f1.__dict__)
    
    #__delattr__删除属性的时候会触发
    f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
    del f1.a
    print(f1.__dict__)
    
    #__getattr__只有在使用点调用属性且属性不存在的时候才会触发
    f1.xxxxxx
  • 相关阅读:
    [LeetCode] 329. Longest Increasing Path in a Matrix
    [LeetCode] 1180. Count Substrings with Only One Distinct Letter
    [LeetCode] 1100. Find K-Length Substrings With No Repeated Characters
    [LeetCode] 312. Burst Balloons
    [LeetCode] 674. Longest Continuous Increasing Subsequence
    [LeetCode] 325. Maximum Size Subarray Sum Equals k
    [LeetCode] 904. Fruit Into Baskets
    [LeetCode] 68. Text Justification
    [LeetCode] 65. Valid Number
    [LeetCode] 785. Is Graph Bipartite?
  • 原文地址:https://www.cnblogs.com/wukai66/p/11252059.html
Copyright © 2020-2023  润新知