1.什么是封装:?
封:属性对外是隐藏的,但对内是开放的。
装:申请一个名称空间,往里装入一系列名字/属性。(类和对象都是有一个名称空间,往里面装一系列的名字)
2、为什么要封装
封装数据属性的目的
首先定义属性的目的就是为了给类外部的使用使用的,
隐藏之后是为了不让外部使用直接使用,需要类内部开辟一个接口
然后让类外部的使用通过接口来间接地操作隐藏的属性。
精髓在于:我们可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作
封装函数属性
首先定义属性的目的就是为了给类外部的使用使用的,
隐藏函数属性是为了不让外不直接使用,需要类内部开辟一个接口
然后在接口内去调用隐藏的功能
精髓在于:隔离了复杂度
3、如何封装
注意:
如何隐藏?
再属性前面加__开头
重点:这种隐藏式对外不对内的,即再类的内部可以直接访问,而在类的外部则无法直接访问。原因是在类定义阶段,类体内代码
统一发生了一次变形。
下面是证明上面一行话的例子:
一种情况:
再类的里面隐藏数据属性,类再内部可以访问到这个隐藏的属性,类再类的外部就访问不到这个隐藏的属性了
class People:
__county='china' #再想隐藏的属性前面加__开头
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def eat(self):
print('eat...')
print(People.__county) #再类的内部调用改变过的属性。通过类名调用属性。
peo1=People('egon',18,'male')
# People.eat(111) #调用函数后,print(People.__county)能访问到,说明对内能访问到
print(People.__county) #再外部直接访问改变后的属性,会报错
二种情况:
# 再类的里面隐藏数据属性或者函数属性,对象再内部可以访问到隐藏的属性,对象再类的外部就访问不到隐藏的属性。
# class People:
# __county='china' #再想隐藏的属性前面加__开头
# def __init__(self,name,age,sex):
# self.__name=name #隐藏属性名
# self.age=age
# self.sex=sex
# def eat(self):
# print('eat...')
# print(self.__name) #对象在类内部调用隐藏的属性
# peo1=People('egon',18,'male')
# peo1.eat() #验证对象在类内部可以访问到隐藏的属性。
# print(peo1.__name) #验证对象在类外部访问不到隐藏的属性。
封装的底层原理
2.这种语法上的变形只在定义阶段发生一次,因为类体代码仅仅只在类定义阶段检查一次
例如:
# 只要是__开头的属性,它在往名称空间里面丢的时候,都做了一个变形。成了_类名__属性名
class People:
__county='china' #_People__county
__n=100
def __init__(self,name,age,sex):
self.__name=name #
self.age=age
self.sex=sex
def eat(self):
print('eat...')
# print(People.__county)
print(self.__county)
s1=People('egon',18,'male')
s1.eat()
# print(People.__dict__) #查看People里面的隐藏属性都做了变形,成了_People__county
print(People._People__county) #直接访问这个变了形的属性名,可以直接访问到
People.__x=1 #再这里__x不会变形,因为这个变形只在类定义阶段发生一次,定义阶段变形后,其他再操作__开头都没用了
print(People.__dict__)
# 总结:这种隐藏仅仅是一种语法上的变形操作。
为什么在内部可以访问到被隐藏的属性?
因为类再定义阶段就会执行类体代码,执行之前会先检测语法,这个检测只在类定义阶段发生一次
再内部能访问到,是因为内部的访问再当初检测语法的时候,都一块变形了。都变成了
最真实的形式
实例训练:
class Foo:
def __f1(self): #_Foo__f1
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.__f1() #self._Foo__f1()
class Bar(Foo):
def __f1(self): #_Bar__f1
print('Bar.f1')
obj=Bar()
obj.f2()
结果是:
Foo.f2
Foo.f1
总结:如果不想让子类的方法覆盖父类的,可以该方法名前加一个__开头。
用隐藏属性这个方法,是如何用呢?
封装数据属性的目的
首先定义属性的目的就是为了给类外部的使用使用的,
隐藏之后是为了不让外部使用直接使用,需要类内部开辟一个接口
然后让类外部的使用通过接口来间接地操作隐藏的属性。
精髓在于:我们可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作
# 想严格控制属性的类型。要做两步:
# 一步是:隐藏属性,外部看不到,想看设计者可以在内部开一个接口,
# 直接用这个接口,再接口内附加一些控制的逻辑
def set_info(self,name,age): #开一个接口,可以改里面的属性。
if type(name) is not str:
# print('用户名必须为str类型')
# return
raise TypeError('用户名必须为str类型')
if type(age) is not int:
# print('年龄必须为int类型')
# return
raise TypeError('年龄必须为int类型')
self.__name=name
self.__age=age
peo1=People('egon',18)
# peo1.name=123 #再原来没有隐藏属性的时候,类型可以随便用,没限制,123也没关系
# peo1.age
# peo1.tell_info()
peo1.set_info('egon',19)
# peo1.tell_info()