封装
隐藏对象的某些属性和实现的细节,仅仅只对外提供公共访问的方式。将函数和属性装到了一个非全局的命名空间。
封装的好处
(1)将变化隔离
(2)便于使用
(3)提高复用性
(4)提高安全性
封装原则
(1)将不需要对外提供的内容全部都隐藏起来
(2)吧属性都隐藏,提供公共方法对其访问
私有变量和私有方法
私有变量:不能在类的外面去引用它。
它依然存在于__dict__中,我们仍然可以调用到。只是python对其的名字进行了修改: _类名__名字。但是在类的外部调用 :需要“_类名__名字”去使用,在类的内部可以正常的使用名字
在python中定义一个私有的名字 :使用两条下划线开头的方式来定义变量名称 __N = ‘aaa’
class Student:
__id = 0 #变成了私有的静态变量类的数据属性就应该是
# 共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
def __init__(self, name, age):
self.name = name #变形为self._Student__name ,私有属性
self.age = age #变形为self._Student__age ,私有属性
def func(self):
print(Student.__id) #在类的内部使用正常
s = Student('hh', 18)
s.func()
print(Student.__id) # 在类的外部直接使用 报错
print(Student._Student__id) #不报错
由此,私有变量只能在类的内部访问的到,外部不能访问,但是,由于它是一个变形的方式。其实可以通过变形以后的方式进行访问,但严格意义上来讲,我们不能通过它来访问,这时只有我们程序员知道就可以了
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
私有方法
私有方法的定义和私有变量一样,只需要在前面加上双下划线,就是标志着私有方法
class Student:
def func(self):
print("我是一个好学生") # 在类的内部使用正常
def __ADCa(self):
print("我喜欢喝ADCa")
s = Student()
s.func() #普通方法调用
s._Student__ADCa() #私有方法调用
s.__ADCa #这样调用,直接报错
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#正常情况,方法都是普通方法
class Dad:
def dear(self):
print('from Dad')
def father(self):
self.dear()
class Son(Dad):
def dear(self):
print('from Son')
s = Son()
s.father()
#当某一个方法是私有的时候,子类就不能继承父类的私有方法了
class Dad:
def dear(self):
print('from Dad')
def __father(self):
self.dear()
class Son(Dad):
def dear(self):
print('from Son')
s = Son()
s.father()
总之,在类中,静态属性,方法,对象属性都可以变成私有的,只需要在这些名字之前加上__
私有的名字,在类内使用的时候,就是会变形成_该类名__方法名。下面这个例子:
以此为例 :没有双下换线会先找E中的func,但是有了双下划线,会在调用这个名字的类D中直接找_D__func
class D:
def __init__(self):
self.__func()
def __func(self):
print('in D')
class E(D):
def __func(self):
print('in E')
e = E() #结果:<em id="__mceDel">in D</em><em id="__mceDel" style="font-size: 1.5em; background-color: #ffffff; font-family: "PingFang SC", "Helvetica Neue", Helvetica, Arial, sans-serif"> </em>
封装与扩展性
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
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): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,<br> #只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
return self.__width * self.__length * self.__high #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能