本文参考:面向对象之封装
封装
面向对象三大特性最核心的一个特性
封装<—>整合
将封装的属性进行隐藏操作
如何隐藏属性:在属性名前加__
前缀,就会实现一个对外隐藏属性效果
class Foo:
__x = 1
def __f1(self):
print('hello world')
'''
print(Foo.__x)
AttributeError: type object 'Foo' has no attribute '__x'
'''
其实这仅仅只是一种变形操作且仅仅只在类定义阶段发生变形,查看一下:
print(Foo.__dict__)
'''
'_Foo__x': 1, '_Foo__f1': <function Foo.__f1 at 0x0000013977EC9430>
'''
可以发现:类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式
变形后的属性格式,依然可以被访问
print(Foo._Foo__x)
print(Foo._Foo__f1)
'''
1
<function Foo.__f1 at 0x0000019647CB9430>
'''
并且这种变形对外不对内,在类内部可以直接访问.
为何呢? 类在定义阶段会进行语法检测,一旦发现了_ _开头的属性就会直接变形为 _Foo__
,而到了类外部时,类的变形已经完成了,所以在类外部无法访问
class Foo:
__x = 1
def __f1(self):
print('hello world')
def f2(self):
print(self.__x)
print(self.__f1)
obj = Foo()
obj.f2()
'''
1
<bound method Foo.__f1 of <__main__.Foo object at 0x0000016660539AF0>>
'''
为何要隐藏属性?
封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性,那这么做的意义何在???
- 隐藏数据属性
将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。
如果不进行隐藏数据:外部使用者就可以直接对对象的name属性进行修改,会直接绕过字符格式限时。
class Teacher:
def __init__(self, name, age):
if not isinstance(name, str):
raise TypeError('姓名必须是字符串类型')
if not isinstance(age, int):
raise TypeError('年龄必须是整型')
self.name = name
self.age = age
def tell_info(self):
print('姓名:%s,年龄:%s' % (self.name, self.age))
t = Teacher('egon', 18)
t.tell_info()
t.name = 18
t.age = 'egon'
t.tell_info()
'''
姓名:egon,年龄:18
姓名:18,年龄:egon
'''
如果隐藏数据:t.name 无法直接使用.name,使用修改名字就必须通过set_info,就会受到字符类型限制
class Teacher:
def __init__(self, name, age):
# self.name = name
# self.age = age
self.set_info(name, 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('egon',18)
t.tell_info()
#t.name 无法直接使用.name,使用修改名字就必须通过set_info,就会受到字符类型限制
- 隐藏函数/方法属性
目的是隔离复杂度,有一些方法只需要在类内部使用就可,降低了操作者的操作难度,操作者只需要使用一些大的功能,小的功能在设计时就已经完成,且不被操作者看到。
例子:
取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
隔离了复杂度,同时也提升了安全性。
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性
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()
a=ATM()
a.withdraw()
特性(property)
property是一个装饰器,是用来干什么?
property第一种使用方式
例子:
BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)÷身高^2(m)
class People:
def __init__(self, name, weight, height):
self.name = name
self.height = height
self.weight = weight
def bmi(self):
return self.weight / (self.height ** 2)
obj1 = People('egon', 66, 1.5)
print(obj1.bmi())
将bmi定义成函数的原因:
- 从bmi的公式上看,bmi应该是触发功能计算得到的
- bmi是随着身高,体重的变化而动态变化的,不是一个固定的值,也就是每次都需要计算而得来
所以在使用的时候需要(),但是bmi听起来更像是一个数据,而非功能
就会造成使用者在使用bmi时,会缺少( ),所以需要减少误解。就引入了property装饰器,这样使用者在使用过程中,就可以不使用( )
@property
def bmi(self):
return self.weight / (self.height ** 2)
obj1 = People('egon', 66, 1.5)
print(obj1.bmi)
'''
29.333333333333332
'''
property第二种使用方式
例子:
通过封装的学习之后,会了解到开发者会将某些属性隐藏起来,而限制使用者对数据查看,修改。但是这种限制也有一点的缺点,就是增加了操作者对于数据操作的复杂度,如果操作者像通过对数据进行修改,就一定要调用开发者设置的功能,而每个开发者设置的功能名也许不同,就导致不必要的麻烦。
class People:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_info(self, name, age):
if not isinstance(name, str):
raise TypeError('姓名必须是字符串类型')
self.__name = name
def del_name(self):
print('不让删除')
pass
#产生对象
t = People('egon')
#修改对象
t.set_info('haha')
#查看对象
print(t.get_name())
有没有一种统一的方法,使得使用者在操作数据时,无需先理解开发者的功能名,而可以按照以往的逻辑方式进行修改,如下例子:
#查看对象
print(t.name)
#修改对象
t.name = 'haha'
#删除对象
del t.name
使用property装饰器
class People:
def __init__(self, name):
self.__name = name
@property # name=propert(name)
def name(self):
return self.__name
@name.setter
def name(self, name):
if not isinstance(name, str):
raise TypeError('姓名必须是字符串类型')
self.__name = name
@name.deleter
def name(self):
print('不让删除')
pass
# name = p()
t = People('egon')
t.name = 'haha'
print(t.name)
del t.name
'''
haha
不让删除
'''