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

    封装的概念

    装指的是把属性装进一个容器,封指的是隐藏的意思,但是这种隐藏是对外隐藏,并不对内部隐藏。

    如何封装

    只需要在属性前加上 __ 开头,该属性就会被隐藏起来,该隐藏具备的特点有:

      1、只是一种语法意义上的变形,即 __ 开头的属性会在检测语法时发生 “ _类名__属性名 ” 的变形

      2、这种隐藏式是对外不对内的,因为在类的内部检测语法时所有的代码统一都发生了变形

      3、这种变形只在检测语法时发生一次,在类定义之后新增的 __ 开头的属性并不会发生变形

      4、如果父类不想让子类覆盖自己的属性,可以在属性前加 __ 开头

    class Foo():
    
        __x = 111   # _Foo__x
    
        def __init__(self, m, n):
            self.__m = m    # self._Foo__m=m
            self.n = n
    
        def __func(self):  # _Foo__func
            print('Foo.func')
    
        def func1(self):
            print(self.__m)  # self._Foo__m
            print(self.__x)  # self._Foo__x
    
    print(Foo.__dict__)
    # Foo.__x   # 报错 AttributeError: type object 'Foo' has no attribute '__x'
    print(Foo._Foo__x)
    封装
    class Foo:
        def __f1(self):  # _Foo__f1
            print('Foo.f1')
    
        def f2(self):
            print('Foo.f2')
            self.__f1()  # self._Foo__f1
    
    class Bar(Foo):
        def __f1(self):  # _Bar__f1
            print('Bar.f1')
    
    obj = Bar()
    obj.f2()
    
    # 运行
    Foo.f2
    Foo.f1
    封装

    封装数据属性的真实意图

    对于数据属性(属性),它的目的是将数据属性封装起来,在类外部的使用就无法直接操作该数据属性,而是需要从类的内部开一个接口给使用者,类的设计者可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作。

    class People():
        def __init__(self, name):
            self.__name = name
    
        def tell_name(self):
            print('name: %s' %self.__name)
    
    obj = People('湫兮')
    
    obj.tell_name()
    
    # 运行
    name: 湫兮
    查看

    上面的查看名字,可以控制输出格式,既然是控制,那就不仅仅是只有查看,还有增加、删除、修改的操作,但要注意,针对某一属性没有增加的说法,比如说对于这个属性的名字,肯定没有为这个属性增加名字的说法,因此只能删除和修改

    class People():
        def __init__(self, name):
            self.__name = name
    
        def tell_name(self):
            print('name: %s' %self.__name)
    
        def set_name(self, new_name):
            self.__name = new_name
    
    obj = People('湫兮')
    
    obj.tell_name()
    obj.set_name('qiuxi')
    
    obj.tell_name()
    修改

    上面是将名字 “湫兮” 改为 “qiuxi”,但这样写并不严谨,因为我不管改成什么类型都可以修改,而名字是一个字符串,因此要加上判断,如果修改的名字不是字符串类型那就输出提示信息,并且无法修改

    class People():
        def __init__(self, name):
            self.__name = name
    
        def tell_name(self):
            print('name: %s' %self.__name)
    
        def set_name(self, new_name):
            if type(new_name) is not str:
                print("名字必须是字符串类型")
                return
            self.__name = new_name
    
    obj = People('湫兮')
    
    obj.tell_name()
    obj.set_name(123)
    
    obj.tell_name()
    
    # 运行
    name: 湫兮
    名字必须是字符串类型
    name: 湫兮
    严谨的修改

    删除也是一样的操作

    class People():
        def __init__(self, name):
            self.__name = name
    
        def tell_name(self):
            print('name: %s' %self.__name)
    
        def set_name(self, new_name):
            if type(new_name) is not str:
                print("名字必须是字符串类型")
                return
            self.__name = new_name
    
        def del_name(self):
            del self.__name
    
    obj = People('湫兮')
    
    obj.tell_name()
    obj.set_name("qiuxi")
    
    obj.tell_name()
    
    obj.del_name()
    
    print(obj.__dict__)
    
    # 运行
    name: 湫兮
    name: qiuxi
    {}
    删除

    封装函数属性的真实意图

    对于函数属性(方法),封装的目的是为了隔离复杂度。好比电脑开机,外部操作你只需要按下开机键,然后等待一会就可以使用了,但是在内部,从你按下开机键,它会做很多操作,比如电源供电、执行 BIOS 程序等,这些在内部是自动操作的。这就是一种封装的思想,按下开机键的背后封装了一堆的功能

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

    property 装饰器

    成人的 BMI 数值:
    过轻:低于18.5
    正常:18.5-23.9
    过重:24-27
    肥胖:28-32
    非常肥胖, 高于32
      体脂参数(BMI)= 体重(kg)÷ 身高^2(m)
      EX:70kg÷(1.75×1.75)= 22.86
    class People():
        def __init__(self, name, weight, height):
            self.name = name
            self.weight = weight
            self.height = height
    
        # 体脂参数不是一个固定的值, 而是通过身高体重计算出来的
        # 所以不能写在__init__里面作为对象的属性
        def tell_bmi(self):
            return self.weight / (self.height ** 2)
    
    obj = People("qiuxi", 66, 1.74)
    print(obj.tell_bmi())
    View Code

    这样写是能计算BMI了,但仔细想想,BMI 更像是一个人的数据属性,它是一个人的特征,此时在这里获取 BMI 却要通过调用 tell_bmi() 才能得到,这样写并不妥。可以设计一个解决方案,首先它的背后一定是计算 BMI 的功能,但是想要在访问的时候,把它伪装成数据属性能够直接访问。这个解决方案就是使用 property 装饰器

    class People():
        def __init__(self, name, weight, height):
            self.name = name
            self.weight = weight
            self.height = height
    
        @property
        def bmi(self):
            return self.weight / (self.height ** 2)
    
    obj = People("qiuxi", 66, 1.74)
    print(obj.bmi)
    property

    现在只是把函数属性伪装成数据属性去访问它,而对于数据属性,还有修改和删除操作。在数据属性中,修改就是直接赋值的操作,但在这里它不能直接赋值,因为它不是一个真正的数据属性,但我就是要修改,该怎么操作呢,这里我换一个例子

    class People():
        def __init__(self, name):
            self.__name = name
    
        @property
        def name(self):
            return self.__name
    
    obj = People("qiuxi")
    # 访问伪装成数据属性的name
    print(obj.name)
    View Code

    但凡是被 property 装饰过的函数,这个函数下面都会生成一个新的装饰器 setter,setter 下面的函数名需要相同,因为对于使用者来说,操作都是使用 name 属性,只是查看和修改对应的功能不同

    class People():
        def __init__(self, name):
            self.__name = name
    
        # 查看
        @property
        def name(self):
            return self.__name
    
        # 修改
        @name.setter
        def name(self, new_name):
            if type(new_name) is not str:
                print("名字必须是字符串类型")
                return
            self.__name = new_name
    
    obj = People("qiuxi")
    print(obj.name)
    
    obj.name = "湫兮"
    print(obj.name)
    View Code

    修改也是相同的操作,被 property 装饰过的函数下面会生成一个新的装饰器 deleter

    class People():
        def __init__(self, name):
            self.__name = name
    
        # 查看
        @property
        def name(self):
            return self.__name
    
        # 修改
        @name.setter
        def name(self, new_name):
            if type(new_name) is not str:
                print("名字必须是字符串类型")
                return
            self.__name = new_name
    
        # 删除
        @name.deleter
        def name(self):
            del self.__name
    
    obj = People("qiuxi")
    print(obj.name)
    
    obj.name = "湫兮"
    print(obj.name)
    
    del obj.name
    print(obj.__dict__)
    View Code

    上面这三种操作是一种新式写法,还有一种旧式写法

    class People():
        def __init__(self, name):
            self.__name = name
    
        # 查看
        def get_name(self):
            return self.__name
    
        # 删除
        def del_name(self):
            del self.__name
    
        # 修改
        def set_name(self, new_name):
            if type(new_name) is not str:
                print("名字必须是字符串类型")
                return
            self.__name = new_name
    
        # 上面定义的顺序可以打乱, 但是这里property里面必须是查看、修改、删除的顺序
        name = property(get_name, set_name, del_name)
    
    
    obj = People("qiuxi")
    print(obj.name)
    
    obj.name = "湫兮"
    print(obj.name)
    
    del obj.name
    print(obj.__dict__)
    View Code

    建议使用新式写法

    以上就是封装的全部介绍,对于封装好的一个属性或方法,没事不要去访问,既然它封装了,目的就是为了不让你去访问它,然而你还去访问,这显然是不合理的

  • 相关阅读:
    ecshop学习入门
    php跳转页面代码
    PHP验证登录用户名和密码
    使用self关键字调用类体中的静态成员
    PHP中读取文件的几个方法
    MVC5 Entity Framework学习之更新相关数据
    global.asax文件的应用
    DbSet<TEntity> 类
    IDisposable接口详解
    bzoj 3240: [Noi2013]矩阵游戏 矩阵乘法+十进制快速幂+常数优化
  • 原文地址:https://www.cnblogs.com/qiuxirufeng/p/9846258.html
Copyright © 2011-2022 走看看