首页 > 其他分享 >封装

封装

时间:2023-01-30 20:11:44浏览次数:32  
标签:__ ... .__ 封装 name self Foo

引入

面向对象编程有三大特性:封装、继承、多态

隐藏属性

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(obj.__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

三 开放接口

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

3.1 隐藏数据属性

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

>>> class Teacher:
...     def __init__(self,name,age): #将名字和年纪都隐藏起来
...         self.__name=name
...         self.__age=age
...     def tell_info(self): #对外提供访问老师信息的接口
...         print('姓名:%s,年龄:%s' %(self.__name,self.__age))
...     def set_info(self,name,age): #对外提供设置老师信息的接口,并附加类型检查的逻辑
...         if not isinstance(name,str):
...             raise TypeError('姓名必须是字符串类型')
...         if not isinstance(age,int):
...             raise TypeError('年龄必须是整型')
...         self.__name=name
...         self.__age=age
... 
>>>
>>> t=Teacher('jason',18)
>>> t.set_info(‘jason','19') # 年龄不为整型,抛出异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in set_info
TypeError: 年龄必须是整型
>>> t.set_info('jason',19) # 名字为字符串类型,年龄为整形,可以正常设置
>>> t.tell_info() # 查看老师的信息
姓名:jason,年龄:19
 

隐藏函数属性

目的是为了隔离复杂度,例如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) = 体重(kg) / 身高**2(m)
EX: 70kg/(1.75*1.75) = 22.86

身高或体重是不断变化的,因而每次想查看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('jason',75,1.85)
>>> obj.bmi #触发方法bmi的执行,将obj自动传给self,执行后返回值作为本次引用的结果
21.913805697589478

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

>>> 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('jason')
>>> f.name
jason
>>> f.name='jason' #触发name.setter装饰器对应的函数name(f,’jason')
>>> f.name=123 #触发name.setter对应的的函数name(f,123),抛出异常TypeError
>>> del f.name #触发name.deleter对应的函数name(f),抛出异常PermissionError

标签:__,...,.__,封装,name,self,Foo
From: https://www.cnblogs.com/DragonY/p/17077142.html

相关文章

  • linux基础:4、文件相关信息、链接信息、系统时间、机器克隆、定时任务、paramiko模块相
    目录一、文件相关信息二、文件索引信息三、链接信息四、系统时间五、机器克隆六、定时任务七、paramiko模块八、公钥私钥九、paramiko其他操作十、代码封装十一、面试题回......
  • 微信小程序组件封装传值以及问题点规避
    封装组件1、在components文件加下新建组件文件夹,在该文件夹下新建组件2、在app.json中注册该组件"usingComponents":{"page":"components/auth/index","com......
  • tcp/ip详解之04 以太网和802.3封装
    1.以太网和802.3封装在TCP/IP世界中,以太网IP数据报的封装是在RFC894[Hornig1984]中定义的,IEEE802网络的IP数据报封装是在RFC1042[PostelandReynolds1988]中定义的。......
  • 原生微信小程序封装request
    request文件//封装请求constbaseURL='https://api-hmugo-web.itheima.net/api/public/v1'constrequest=(options)=>{//returnnewPrimise才可以使用the......
  • 基于jssip封装的软电话功能模块SoftPhoneForWebrtc.ts
    SoftPhoneForWebrtc.ts : importJsSIPfrom'jssip'//自定义数据进行通信的socket事件functionselfSocketEvent(data){ if(window.$ak.socketMyself){ ......
  • 封装用于循环播放展示一组内容的组件
    <template><divv-if="show"class="command-code":style="{color:color}"><divclass="command-code-box"ref="boxRef":style="{width:contentWidth}">......
  • jQuery_1_封装原理
    jQuery是使用JavaScript 编写的函数库,用于简化了JavaScript编程。 问题:在一个页面中,无论是引入外部的js代码,还是内部分开写的js代码块,在此页面解析执行的时候,......
  • tcp/ip详解之02 tcp/ip封装
    1.tcp/ip封装 当应用程序用TCP传送数据时,数据被送入协议栈中,然后逐个通过每一层直到被当作一串比特流送入网络。其中每一层对收到的数据都要增加一些首部信息(有时还要......
  • JDBC之封装工具类(跨平台方案)
    JDBC之封装工具类(跨平台方案)把工具类的配置信息存储在配置文件中,需要修改用户信息时,改配置文件即可在src新建一个resouse配置文件,把工具类中的配置信息写入完善后......
  • uView2.0 对http进行封装
    request.js://此vm参数为页面的实例,可以通过它引用vuex中的变量import{$config}from'@/config/config';module.exports=(vm)=>{//初始化请求配置uni.$......