zoukankan      html  css  js  c++  java
  • python基础24——封装&property

    封装

    一:封装介绍


    封装是面向对象三大特性最核心的一个特性
    封装<->整合




    二、将封装的属性进行隐藏操作

    1、如何隐藏:在属性名前加__前缀,就会实现一个对外隐藏属性效果

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

    class Foo:
    __x = 1 # _Foo__x

    def __f1(self): # _Foo__f1
    print('from test')


    print(Foo.__dict__)
    print(Foo._Foo__x)
    print(Foo._Foo__f1)





    II:这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形

    class Foo:
    __x = 1 # _Foo__x = 1

    def __f1(self): # _Foo__f1
    print('from test')

    def f2(self):
    print(self.__x) # print(self._Foo__x)
    print(self.__f1) # print(self._Foo__f1)

    print(Foo.__x)
    print(Foo.__f1)
    obj=Foo()
    obj.f2()






    III: 这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头的属性都不会变形

    class Foo:
    __x = 1 # _Foo__x = 1

    def __f1(self): # _Foo__f1
    print('from test')

    def f2(self):
    print(self.__x) # print(self._Foo__x)
    print(self.__f1) # print(self._Foo__f1)

    Foo.__y=3
    print(Foo.__dict__)
    print(Foo.__y)

    class Foo:
    __x = 1 # _Foo__x = 1

    def __init__(self,name,age):
    self.__name=name
    self.__age=age

    obj=Foo('egon',18)
    print(obj.__dict__)
    print(obj.name,obj.age)





    2、为何要隐藏?

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

    # 设计者:egon

    class People:
    def __init__(self, name):
    self.__name = name

    def get_name(self):
    # 通过该接口就可以间接地访问到名字属性
    print('小垃圾,不让看')
    print(self.__name)

    def set_name(self,val):
    if type(val) is not str:
    print('小垃圾,必须传字符串类型')
    return
    self.__name=val


    # 使用者:王鹏
    obj = People('egon')
    print(obj.name) # 无法直接用名字属性
    obj.set_name('EGON')
    obj.set_name(123123123)
    obj.get_name()




    II、隐藏函数/方法属性:目的的是为了隔离复杂度


    例如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()

    总结:

    隐藏属性与开放接口,本质就是为了明确地区分内外,类内部可以修改封装内的东西而不影响外部调用者的代码;而类外部只需拿到一个接口,只要接口名、参数不变,则无论设计者如何改变内部实现代码,使用者均无需改变代码。这就提供一个良好的合作基础,只要接口这个基础约定不变,则代码的修改不足为虑

    property

    身高或体重是不断变化的,因而每次想查看BMI值都需要通过计算才能得到,但很明显BMI听起来更像是一个特征而非功能,为此Python专门提供了一个装饰器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('lili',75,1.85)
    >>> obj.bmi #触发方法bmi的执行,将obj自动传给self,执行后返回值作为本次引用的结果
    21.913805697589478

    使用property有效地保证了属性访问的一致性。另外property还提供设置和删除属性的功能,如下

    @name.setter    修改

    @name.deleter      删除

    >>> class Foo:
    ...     def __init__(self,val):
    ...         self.__NAME=val #将属性隐藏起来
    ...     @property
    ...     def name(self):
    ...         return self.__NAME
    ...     @name.setter
    ...     def name(self,value):
    ...         if not isinstance(value,str):  #在设定值之前进行类型检查
    ...             raise TypeError('%s must be str' %value)
    ...         self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
    ...     @name.deleter
    ...     def name(self):
    ...         raise PermissionError('Can not delete')
    ...
    >>> f=Foo('lili')
    >>> f.name
    lili
    >>> f.name='LiLi' #触发name.setter装饰器对应的函数name(f,’Egon')
    >>> f.name=123 #触发name.setter对应的的函数name(f,123),抛出异常TypeError
    >>> del f.name #触发name.deleter对应的函数name(f),抛出异常PermissionError

    使用property的情况:

    本质属于封装,当想得到一个需要计算得到的值,并且需要用数据属性访问的时候就需要用到property

  • 相关阅读:
    Java8之Optional用法举例
    Java多线程之ThreadPoolTaskExecutor用法
    Java多线程之ExecutorService使用说明
    CountDownLatch同步计数器使用说明
    读取excel文件内容 (hutool-poi)
    字符串工具-StrUtil(hutool)
    IDEA 常用插件
    在 Gerrit 仓库中创建空分支
    Linux idea 输入中文出现下划线乱码
    ArchLinux 修改 MariaDB 数据库路径后启动报错 Can't create test file /xxxxx/xxxxx-test
  • 原文地址:https://www.cnblogs.com/lucky-cat233/p/12660273.html
Copyright © 2011-2022 走看看