• Python的面向对象的三大特性之封装


    一、封装

    1、封装的介绍:封装==整合

    2、对于封装的两步操作:隐藏和开放接口

      隐藏属性:

       Python的Class机制采用双下划线开头的方式将属性隐藏起来(设置成私有的),但其实这仅仅只是一种变形操作,类中所有双下滑线开头的属性都会在类定义阶段、检测语法时自动变成“_类名__属性名”的形式:

    class Foo:
        __N=0 # 变形为_Foo__N
    
        def __init__(self): # 定义函数时,会检测函数语法,所以__开头的属性也会变形
            self.__x=10 # 变形为self._Foo__x
    
        def __f1(self): # 变形为_Foo__f1
            print('__f1 run')
    
        def f2(self):  # 定义函数时,会检测函数语法,所以__开头的属性也会变形
            self.__f1() #变形为self._Foo__f1()
    
    print(Foo.__N) # 报错AttributeError:类Foo没有属性__N
    
    obj = Foo()
    print(obbj.__x) # 报错AttributeError:对象obj没有属性__x

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

    (1)在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如Foo._A__N,所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。

    >>> Foo.__dict__
    mappingproxy({..., '_Foo__N': 0, ...})
    
    >>> obj.__dict__
    {'_Foo__x': 10}
    
    >>> Foo._Foo__N
    0
    >>> obj._Foo__x
    10
    >>> obj._Foo__N
    0

    (2)在类内部是可以直接访问双下划线开头的属性的,比如self.__f1(),因为在类定义阶段类内部双下滑线开头的属性同意发生了变形。

    >>> obj.f2()
    __f1 run

    (3)变形操作只在类定义阶段发生一次,在类定义之后的赋值操作,不会变形。

    >>> Foo.__M=100
    >>> Foo.__dict__
    mappingproxy({..., '__M': 100,...})
    >>> Foo.__M
    100
    
    >>> obj.__y=20
    >>> obj.__dict__
    {'__y': 20, '_Foo__x': 10}
    >>> obj.__y
    20

    开放接口

    定义属性就是为了使用,所以隐藏并不是目的

    隐藏数据属性

    将数据隐藏起来就限制了类外部对数据的直线操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格的控制。

    # 设计者
    class People:
        def __init__(self,name):
            self.__name = name
        # 间接使用,定义一个间接隐藏接口:作用:可以在里面加入逻辑
        def get_name(self):
            print("看见名字了吗?")
            print(self.__name)
    
        def set_name(self,val):
            self.__name = val
    # 使用者
    obj = People('lsj')
    # print(obj.name)  # lsj  当隐藏了name属性后,外界不能直接使用,间接使用
    obj.get_name()  # lsj
    
    print('='*50)
    obj.set_name("LSJ")
    obj.get_name()
    
    # 接口的好处:
    # 1、利用对外隐藏对内不隐藏可以加入逻辑

    隐藏函数属性

    目的的是为了隔离复杂度,例如ATM程序的取款功能,该功能有很多其他功能组成,比如插卡、身份认证、输入金额、打印小票、取钱等,而对使用者来说,只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来。

    >>> 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()
    ...
    >>> obj=ATM()
    >>> obj.withdraw()

     3、property装饰器(面向对象的延申探讨)

    # 装饰器是在不修改被装饰对象源代码以及调用方式的前提下
    # 被装饰对象添加新功能的可调用对象
    
    print(property)  # 这个装饰器是用类实现的<class 'property'>
    
    # 想了解property面向对象高级在最后拔高的时候学习
    
    # property是一个装饰器,是用来干什么的?
    # property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性
    
    """
    BMI指数是用来衡量一个人的体重与身高对健康影响的一个指标,计算公式为
    成人的BMI数值: 过轻:低于18.5 正常:18.5-23.9 过重:24-27.9 肥胖:28-32 非常肥胖,高于32 体质指数(MBI) = 体重(kg) / 身高^2(m) 例如:70kg➗(1.75*1.75) = 22.86 """ # 假设一个类 class People: # 定义一个人的身高体重 def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height # 定义函数的原因: # 1、从bmi的公式上看,bmi应该触发功能计算得到的 # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值,也即是说每次都是需要临时计算得到的。 # 但是bmi这个名字提起来像一个数据属性,而非功能,所以要吧bmi伪装成功能属性,所以引用property @property def bmi(self): return self.weight / (self.height**2) # 得到一个人的对象 obj = People('lsj',82,1.74) # print(obj.bmi()) # obj.height = 1.75 # print(obj.bmi()) # 使用property装饰器后 print(obj.bmi)

     

    # property 的深入探讨
    # 案例一
    class People:
        def __init__(self,name):
            # 隐藏名字
            self.__name = name
        # 开接口,针对同一种属性的操作有四种,分别是:增删改查
        # 先定义个查询的接口
        def get_name(self):
            return self.__name
        # 定义修改接口
        def set_name(self,val):
            if type(val) is not str:
                print('必须传入str类型')
                return
            self.__name = val
        # 定义删除接口
        def del_name(self):
            print('不让删除')
            # del self.__name
    
    obj = People('lsj')
    print(obj.get_name()) # lsj
    obj.set_name('LSJ')
    print(obj.get_name()) # LSJ
    obj.del_name()        # 不让删除
    print(obj.get_name()) # LSJ

     

    # property 的深入探讨
    # 案例二
    class People:
        def __init__(self,name):
            # 隐藏名字
            self.__name = name
        # 开接口,针对同一种属性的操作有四种,分别是:增删改查
        # 先定义个查询的接口
        # 优化使用装饰器
        # @property
        def get_name(self):
            return self.__name
        # 定义修改接口
        def set_name(self,val):
            if type(val) is not str:
                print('必须传入str类型')
                return
            self.__name = val
        # 定义删除接口
        def del_name(self):
            print('不让删除')
            # del self.__name
    
        # 也可以这样做,使用property对后面的参数进行操作,装饰器的具体步骤
        name = property(get_name,set_name,del_name)
    
    obj = People('lsj')
    # print(obj.get_name()) # lsj
    print(obj.name)  # lsj

     

    # encoding=utf-8
    # auther:lsj
    
    # property 的深入探讨
    # 案例三
    class People:
        def __init__(self,name):
            # 隐藏名字
            self.__name = name
        @property  # name = property(name)
        def name(self): # obj.name
            return self.__name
        # 定义修改接口
        @name.setter
        def name(self,val): # obj.name='LSJ'
            if type(val) is not str:
                print('必须传入str类型')
                return
            self.__name = val
        # 定义删除接口
        @name.deleter
        def name(self): # del obj.name
            print('不让删除')
    
    obj = People('lsj')
    # 查看
    print(obj.name)  # lsj
    # 修改
    obj.name = 'LSJ'
    print(obj.name)  # LSJ
    # 删除
    del obj.name     # 不让删除
    print(obj.name)
    
    # 类似的场景如下
    import subprocess
    # 调用管道
    print(subprocess.PIPE) # -1

     

     

  • 相关阅读:
    Oracle建立表空间和用户
    fscanf()函数具体解释
    三层架构(我的理解及具体分析)
    ListView嵌套ListView优化
    Android xml 解析
    玩转Web之servlet(三)---一张图看懂B/S架构
    jquery.scrollTo-min.js
    C#中MessageBox使用方法大全(附效果图)
    hdu 1882 Strange Billboard(位运算+枚举)
    MySQL 通配符学习小结
  • 原文地址:https://www.cnblogs.com/liunaixu/p/12842350.html
Copyright © 2020-2023  润新知