zoukankan      html  css  js  c++  java
  • 面向对象基础之封装

     封装

    ​ 什么是封装,就是将复杂的丑陋的,隐私的细节隐藏到内部,对外提供简单的使用接口

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

    为什么需要封装

    ​ 两个目的

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

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

    什么时候应该封装

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

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

     如何使用:

    语法:

    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"

    被封装的内容的特点:

    ​ 1.外界不能直接访问

    ​ 2.内部依然可以使用

    权限

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

    在python只要两种权限,

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

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

    在外界访问私有的内容

    属性虽然被封装了,但是还是需要使用的,在外界如何访问

    通过定义方法类完成对私有属性的修改和访问


    这是一个下载器类,需要提供一个缓存大小这样的属性
    缓存大小不能超过内存限制

    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())
    
    d.start_download()

    这样一来我们可以在外界修改这个关键数据时,做一些限制

    property装饰器

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

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

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

    有三个相关的装饰器


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


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

    案例:

    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)
    ```

    python实现封装的原理

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

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

    封装:

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

    好处:

    ​ 1.提高安全性

    ​ 2.隔离复杂度

    语法:将要封装的属性或方法名称前加上双下划线

    访问被隐藏的属性:

    ​ 提供用于访问和修改的方法

    使用property装饰器可以将一个方法伪装成普通顺属性,报纸属性之间调用方法一致

    封装的实现原理 ,替换变量名称

     property 可以用来实现计算属性

    计算属性指的是:属性的值,不能直接获得,必须通过计算才能获取

    例如:正方形求面积

    class Space:
    def __init__(self,width):
    self.width = width
    
    @property
    def area(self):
    return self.width * self.width
    s = Space(10)
    print(s.area)
    
    s.width = 20
    print(s.area)

    练习: 定义一个类叫做person
    包含三个属性 身高 体重 BMI
    BMI的值需要通过计算得来 公式 体重 / 身高的平方

    class Person:
    
    def __init__(self,height,weight):
    self.height = height
    self.weight = weight
    
    @property
    def BMI(self):
    return self.weight/(self.height**2)
    
    
    p = Person(175,50)
    print(p.BMI)
    ```

    接口 (了解)

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

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

    接口目的就是为了提高扩展性:

    例如电脑提前指定制定一套USB接口协议,只要你遵循该协议,你的设备就可以被电脑使用,不需要关心到底是鼠标还是键盘

    案例:

    class USB:
    def open(self):
      pass
    
    def close(self):
      pass
    
    def read(self):
      pass
    
    def write(self):
      pass
    
    class Mouse(USB):
      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(USB):
      def open(self):
        print("键盘开机.....")
    
      def close(self):
        print("键盘关机了...")
    
      def read(self):
        print("获取了按键字符....")
    
      def write(self):
        print("可以写入灯光颜色....")
    
    # 来了一个键盘对象
    k = KeyBoard()
    pc(k)
    ```

    在上述案例中,PC的代码一旦完成,后期无论什么样的设备 只要遵循了USB接口协议,都能够被电脑所调用

    接口主要是方便了对象的使用者,降低使用者的 学习难度,只要学习一套使用方法,就可以以不变应万变

    问题:

    如果子类没有按照你的协议来设计,也没办法限制他,将导致代码无法运行

    抽象类

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

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

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

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

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

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

    案例:

    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)
    

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

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

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

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

    小练习:

    '''
    用面向对象实现 植物大战僵尸游戏
    
    1.定义一个僵尸Zombie类,该类可以实例化出多种僵尸对象,僵尸对象产生默认都有 名字name、血量HP、防具armor
        -- 名字:普通僵尸 | 路障僵尸 | 铁桶僵尸
        -- 血量:默认就是100,不需要外界提供
        -- 防具:不需要外界提供,从名字中分析确定,防具的值是一个列表,从名字分析得到
                -- ['无', 0] | ['路障', 5] | ['铁桶', 15]  => [防具名, 防具的防御值]
        -- 通过@property的getter、setter方式,对外提供防具的两个访问接口armor_name与armor_count
                -- armor_name可以取值、赋值、删除值:通过一个
                    -- eg: 普通僵尸对象.armor_name = '铁桶',不仅改变了防具名
                    --     普通僵尸对象的名字name也会变成 铁桶僵尸
                -- armor_count只可以取值
    '''
    class Zombie:
        def __init__(self,name):
            self.name = name
            self.HP = 100
            if name not in ['普通僵尸','路障僵尸','铁桶僵尸']:
                self.name = '普通僵尸'
                self.__armor = ['',0]
            elif self.name.startswith('普通'):
                self.name = '普通僵尸'
                self.__armor = ['普通',0]
            elif self.name.startswith('路障'):
                self.name = '路障僵尸'
                self.__armor = ['路障', 5]
            elif self.name.startswith('铁桶'):
                self.name = '铁桶僵尸'
                self.__armor = ['铁桶', 15]
        @property
        def armor_name(self):
            return self.__armor
    
        @armor_name.setter
        def armor_name(self,name):
            if name not in ['','路障','铁桶']:
                return
            if name == '':
                name = '普通'
                self.__armor = ['',0]
            elif name == '路障':
                self.__armor = ['路障',0]
            else:
                self.__armor = ['铁桶',0]
    
            self.name = name + '僵尸'
        @armor_name.deleter
        def armor_name(self):
            del self.__armor
        @property
        def armor_count(self):
            return self.__armor[1]
    
    '''
    2.定义一个角色User类,该类有名字name属性、以及打僵尸的beat方法
        -- 名字:随意自定义
        -- beat:该方法需要传入一个僵尸对象
                -- 在方法内部可以实现:某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血
                -- 每一次攻击,都固定扣除25滴血,但是不同的僵尸会被防具相应抵消掉一定的伤害值
                -- 循环攻击僵尸,3s攻击一次,僵尸被击杀后,打印 某某用户击杀了某某个僵尸 并结束方法
    '''
    class User:
        def __init__(self,name):
            self.name = name
        def beat(self,zombie):
            u_name = self.name
            z_name = zombie.name
            z_HP = zombie.HP
            low_HP = 25 - zombie.armor_count
            while z_HP > 0:
                import time
                time.sleep(0.5)
                z_HP -= low_HP
                print(f'{u_name}攻击了{z_name},僵尸损失了{low_HP}滴血,还剩{z_HP}滴血')
            print(f'{u_name}击杀了{z_name}')
    
    '''
    3.定义一个Game类,该类有一个name属性,属性值为 "植物大战僵尸" ,该类中有一个start方法,通过Game.start()来启动游戏
        -- 游戏一开始先显示游戏的名字 植物大战僵尸游戏
        -- 会随机产生三种僵尸,总共产生三个作为要被击杀的对象
        -- 生成一个有角色名的角色,依次去击杀随机产生的每一只僵尸
            -- 开始击杀第一只僵尸 => 某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血 => 某某用户击杀了某某个僵尸 => 第一只僵尸已被击杀完毕 => 开始击杀第二只僵尸 ... => 第三只僵尸已被击杀完毕
    '''
    
    import random
    
    class Ganme:
        name = '东东大战僵尸'
        @classmethod
        def start(cls):
            print(cls.name)
            for i in range(1,5):
                name = random.choice(['普通僵尸','路障僵尸','铁桶僵尸'])
                zombie = Zombie(name)
                user = User('东东')
                print(f'准备开始打第{i}只僵尸')
                user.beat(zombie)
                print(f'第{i}只僵尸已被击杀')
    Ganme.start()
    View Code
  • 相关阅读:
    获取jsonPath的节点名称
    如何删除 macOS High Sierra 上的 swapfile
    Prototype fake it till make it.观后感
    intellij idea 初步环境熟悉
    一个比较综合的项目--》>图片缓存,下拉刷新等
    写的很好的博客->有关性能优化、图片缓存等
    layout优化之-》viewStub,requestFocus,merge,include
    有关ActionBar
    android:installLocation 解析
    Android开发效率—Eclipse快捷键
  • 原文地址:https://www.cnblogs.com/linxidong/p/11250901.html
Copyright © 2011-2022 走看看