28、封装
一、封装介绍
封装、继承、多态是面向对象三大特性中最核心的一个
封装就是将数据和功能整合在一起。
针对封装的属性,将会控制对封装对象的访问,分别是隐藏和开放接口
二、将封装的属性进行隐藏操作
2.1、怎么隐藏
在属性名前加入__前缀,将会实现对外隐藏的效果
2.2、隐藏时需要注意的问题
1、在类的外部无法直接访问到隐藏的属性,但是知道了类的名字和属性名之后就能拼出名字:_类名__属性,接着就能用这个名字访问,这属于一种加密手段,所以没有破解的意义。
class Foo: __x = 1 # _Foo__x #将x进行隐藏 def __f1(self): # _Foo__f1 print('from test') print(Foo.__dict__) #可以访问 print(Foo._Foo__x) #已隐藏,找不到,报错 print(Foo._Foo__f1) #已隐藏,找不到,报错
2、这种隐藏是对外不对内的,因为__开头的属性在检查语法的时候回统一变形
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() #可以访问
3、这种变形操作只会在检查语法的时候发生一次吗,之后定义的都不会发生变形
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)
2.3、为什么要隐藏
为了隔离复杂度,隐藏数据属性,可以限制类外部对数据的直接操作
类内应该提供接口,为类外部的间接提取数据提供对接方式
# 设计者: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()
三、property装饰器
装饰器是在不修改被装饰对象源代码以及调用方式的情况下为被装饰对象添加新功能的可调用对象
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 # 定义函数的原因1: # 1、从bmi的公式上看,bmi应该是触发功能计算得到的 # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值 # 说白了,每次都是需要临时计算得到的 # 但是bmi听起来更像是一个数据属性,而非功能 @property def bmi(self): return self.weight / (self.height ** 2) obj1 = People('egon', 70, 1.83) print(obj1.bmi()) obj1.height=1.86 print(obj1.bmi()) print(obj1.bmi)