python面向对象(二)之封装
封装定义:
在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用。
即“封装”就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类);封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。这就用到私有化,前面简单的讲了下,下面回具体将。
封装的原因:
封装数据的主要原因是:保护隐私(把不想别人知道的东西封装起来)
封装方法的主要原因是:隔离复杂度(比如:电视机,我们看见的就是一个黑匣子,其实里面有很多电器元件,对于用户来说,我们不需要清楚里面都有些元件,电视机把那些电器元件封装在黑匣子里,提供给用户的只是几个按钮接口,用户只需要知道通过按钮就能对电视机的操作就行,而且也避免了用户对其无意识的破环)。
封装的封装层次
封装分为两个层面,但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)。
-
第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式访问里面的名字,这本身就是一种封装。对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口。
-
第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
这个层面的封装就用到了私有化。在python中没有像java中的关键子去控制访问权限而用的是下划线来控制。
python用加双下划线前缀表示私有。其中所有双下划线开头的名称如_ _ x都会自动变形成:_类名 _ _x的形式,根据这个就可以直接调用私用属性和方法,不过不建议那样做。一般使用自己编写的get,set方法去访问或这用property进行访问和设置。在后面的装饰器会讲。
class A:
__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
def __init__(self):
self.__X=10 #变形为self._A__X
def getX(self): #起名字时一般是属性首字母大写再在前面加get或者set
return self.__X
def setX(self,x):
self.__X=x
def __foo(self): #变形为_A__foo
print('from A')
def bar(self):
self.__foo() #只有在类内部才可以通过__foo的形式访问到.
这种自动变形的特点:
1、类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2、这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3、在子类定义的_ _ x不会覆盖在父类定义的_ _ x,因为子类中变形成了:_ 子类名 _ _ x,而父类中变形成了:父类名 _ _ x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
封装的优点:
- 通过隐藏对象的属性来保护对象内部的状态,安全性提高。
- 提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。
- 禁止对象之间的不良交互提高模块化,各取所需,利于分工。
补充知识:
上面讲了双下划线,下来讲下但下划线和前后都有下划线的情况
单下划线开头(_)
在一个模块中以单下划线开头的变量和函数被默认当作内部函数,如果使用from a_module import * 导入时,这部分变量和函数不会被导入。不过值得注意的是,如果使用 import a_module 这样导入模块,仍然可以用a_module._some_var 这样的形式访问到这样的对象。
双下划开头结尾
双下划线开头双下划线结尾的是一些 Python 的特殊对象,如类成员的 _ _ init_ _ ,_ _ del _ _, _ _ new _ _ , _ _str _ _等。** Python 官方推荐永远不要将这样的命名方式应用于自己的变量或函数,**而是按照文档说明来使用。