(一)封装
(一)封装导读
"""封装导读"""
# 封装其实就是讲属性隐藏起来,不然外界发现和使用
# 接口:可以允许外界使用的内容通过接口开放,让用户通过接口使用
# 封装的原因是:保护隐私,将数据保护起来
# 隐藏属性的发给发是通过__变量名实现的
class Student():
__school='清华大学'
def __init__(self,name,age,gender):
self.__name=name
self.__age=age
self.__gender=gender
def read(self):
self.__read()
def __read(self):
print(f"{self.__name}正在读书")
#修改名字和属性
def set_info(self,name,age):
self.__name=name
self.__age=age
s1=Student(name='syh',age=23,gender="female")
s1.read()#syh正在读书
print(s1.__dict__)
#{'_Student__name': 'syh', '_Student__age': 23, '_Student__gender': 'female'}
#修改后的s1 字典
s1.set_info(name='su',age=20)
print(s1.__dict__)
#{'_Student__name': 'su', '_Student__age': 20, '_Student__gender': 'female'}
# 如果后续通过对象新增了一个属性,那这个属性和原来封装起来的属性不是同一个了
s1.__school='郑州大学'
print(s1.__school)#郑州大学
(1)什么是封装
- 在程序设计中,封装(Encapsulation)是对具体对象的一种抽象
- 即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用。
- 要了解封装,离不开“私有化”,就是将类或者是函数中的某些属性限制在某个区域之内,外部无法调用。
(2)为什么要封装
- 封装数据的主要原因是:保护隐私(把不想别人知道的东西封装起来)
(3)封装的方法
- 你的身体没有一处不体现着封装的概念:
- 你的身体把膀胱尿道等等这些尿的功能隐藏了起来,然后为你提供一个尿的接口就可以了(接口就是你的。。。,)
- 你总不能把膀胱挂在身体外面,上厕所的时候就跟别人炫耀:hi,man,你瞅我的膀胱,看看我是怎么尿的。
- 电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!
- 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了
提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
"""封装"""
class Student():
school='清华大学'
name='syh'
age=20
location='河南省洛阳市'
_location='河南省洛阳市'
__location='河南省洛阳市'
def show_info(self):
print(self.location)
print(self._location)
print(self.__location)
def _show_info(self):
print(self.location)
print(self._location)
print(self.__location)
def __show_info(self):
print(self.location)
print(self._location)
print(self.__location)
#实例化得到对象
s1=Student()
#封装数据的属性
print(s1.location)
Student._location
#河南省洛阳市
print(s1._location)
#河南省洛阳市
# print(s1.__location)
#AttributeError: 'Student' object has no attribute '__location'. Did you mean: '_location'?
#封装函数的属性
s1.show_info()
s1._show_info()
# s1.__show_info()
#AttributeError: 'Student' object has no attribute '__show_info'. Did you mean: '_show_info'?
"""总结"""
# 封装数据属性:在类的内部,两个以下的_封装起来的属性,在类的内部可以任意访问,在类的外部(对象)可以访问
# 封装数据属性:在类的内部,两个及以上的_封装起来的属性,在类的内部可以任意访问,但是在类的外部(对象)不可以访问
# 封装函数属性:在类的内部,两个以下封装起来的属性,在类内部可以任意访问,在类外部(对象)可以访问
# 封装函数属性:在类的内部,两个及以上封装起来的属性,在类内部可以任意访问,在类外部(对象)不可以访问
(二)封装之隐藏属性
- 隐藏数据属性
class Student():
#将名字和年龄都隐藏起来
def __init__(self,name,age):
self.__name=name
self.__age=age
# 对外提供访问信息的接口
def interface(self):
print(f"姓名:{self.__name},年龄:{self.__age}")
# 对外设置信息的接口,对信息进行操作
def set_info(self,name,age):
if not age.isdigit():
return "必须是数字"
self.__name=name
self.__age=age
# 实例化得到对象
s1=Student(name='syh',age=23)
#查看对象的信息
s1.interface()# 姓名:syh,年龄:23
#调用对象的方法修改对象的信息
s1.set_info(name='su',age='20')
#查看对象的信息
s1.interface()#姓名:su,年龄:20
- 函数属性
"""函数属性"""
class ATM():
# 插卡
def __card(self):
print("插卡")
#用户认证
def __auth(self):
print("用户认证")
#输入金额
def __input(self):
print("输入金额")
#取款
def __get_balance(self):
print("取款")
#打印账单
def __print_bill(self):
print("打印账单")
# 主要功能接口
def main(self):
self.__card()
self.__auth()
self.__input()
self.__get_balance()
self.__print_bill()
atm=ATM()
atm.main()
# 插卡
# 用户认证
# 输入金额
# 取款
# 打印账单
(三)封装小结
"""封装小结"""
# 封装可以封装两种属性
# 类中有两种属性:数据属性和函数属性
# 封装的两种属性:数据属性和函数属性
class Student():
# 封装数据属性
__school='清华大学'
# 封装函数属性
def __read(self):
print(f"学校名称{self.__school}")
# 封装是为了保护数据的隐私性
# 但是我们也需要修改用户隐私数据的需求
# 所有开放接口来修改隐藏数据
def set_info(self,school):
self.__school=school
self.__read()
#实例化类得到对象
s1=Student()
print(Student.__dict__)
#{'__module__': '__main__', '_Student__school': '清华大学', '_Student__read': <function Student.__read at 0x0000024C47722320>, 'set_info': <function Student.set_info at 0x0000024C47722950>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
print(s1._Student__school)# 清华大学
# 调用set_info修改数据
s1.set_info(school='郑州大学')
#学校名称郑州大学
- 总结隐藏属性与开放接口,本质就是为了明确地区分内外,类内部可以修改封装内的东西而不影响外部调用者的代码;
- 而类外部只需拿到一个接口,只要接口名、参数不变,则无论设计者如何改变内部实现代码,使用者均无需改变代码。
- 这就提供一个良好的合作基础,只要接口这个基础约定不变,则代码的修改不足为虑。
(四)property装饰器
(1)什么是property
- property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
(2)BMI例子
- BMI指数是用来衡量一个人的体重与身高对健康影响的一个指标
- 成人的BMI数值:
- 过轻:低于18.5
- 正常:18.5-23.9
- 过重:24-27
- 肥胖:28-32
- 非常肥胖, 高于32
- 计算公式
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
- 身高或体重是不断变化的
- 因而每次想查看BMI值都需要通过计算才能得到,但很明显BMI听起来更像是一个特征而非功能,为此Python专门提供了一个装饰器property
- 可以将类中的函数“伪装成”对象的数据属性,对象在访问该特殊属性时会触发功能的执行,然后将返回值作为本次访问的结果
class Person():
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property
def bmi(self):
return self.weight/(self.height**2)
p = Person('syh',height=1.8,weight=85)
# 触发bmi正常执行,将p传入到self ,执行后返回值作为本次的结果
# 正常的调用是p。bmi()
# 现在加入property装饰器后,没有加()就调用成功了!
print(p.bmi)#26.234567901234566
(3)为什么要用property
- 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
- 面向对象的封装有三种方式:
- 【public】
- 这种其实就是不封装,是对外公开的
- 【protected】
- 这种封装方式对外不公开
- 但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
- 【private】
- 这种封装对谁都不公开
- 【public】
- python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现
"""方式一"""
class Student():
#将数据隐藏起来
def __init__(self,name1,age):
self.__name=name1
self.__age=age
@property
def name(self):
return self.__name
@name.setter
def name(self,name1):
self.__name=name1
@name.deleter
def name(self):
raise PermissionError('Can not delete')
s=Student(name1='syh',age=23)
print(s.name)
# syh
s.name='su'
print(s.name)
# su
del s.name
#PermissionError: Can not delete
"""方式二"""
class Student():
#将数据隐藏起来
def __init__(self,name):
self.__name=name
# 得到值
def get_name(self):
return self.__name
# 设定值
def set_name(self,name):
self.__name=name
# 删除
def del_name(self):
raise PermissionError('Can not delte')
# 不使用装饰器,而是使用 包装的 形式
name=property(get_name,set_name,del_name)
s=Student(name='syh')
print(s.name)# syh
# 触发name.setter装饰器对应的函数name(s,'su')
s.name='su'
print(s.name)# su
# 触发name.deleter对应的函数name(f),抛出异常PermissionError
del s.name
#PermissionError: Can not delte
(五)封装和扩展性
- 封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;
- 而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。
- 这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
(1)设计者
class Room:
def __init__(self, name, owner, width, length, high):
self.name = name
self.owner = owner
self.__width = width
self.__length = length
self.__high = high
# 对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
def tell_area(self):
return self.__width * self.__length
(2)使用者
class Room:
def __init__(self, name, owner, width, length, high):
self.name = name
self.owner = owner
self.__width = width
self.__length = length
self.__high = high
# 对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
def tell_area(self):
return self.__width * self.__length
# 使用者
room = Room('卧室', 'dream', 20, 20, 20)
# 使用者调用接口tell_area
result = room.tell_area()
print(result)
# 400
(3)更好的扩展性
# 类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
def __init__(self, name, owner, width, length, high):
self.name = name
self.owner = owner
self.__width = width
self.__length = length
self.__high = high
# 对外提供的接口,隐藏内部实现,
# 此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,
# 而且外部调用感知不到,仍然使用该方法,但是功能已经变了
def tell_area(self):
return self.__width * self.__length * self.__high
# 对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
room = Room('卧室', 'dream', 20, 20, 20)
# 使用者调用接口tell_area
result = room.tell_area()
print(result)
# 8000
标签:__,.__,封装,name,self,print,def
From: https://www.cnblogs.com/suyihang/p/17947891