面向对象三大特性之封装
封装
不绕弯子,面向对象的编程思想有三大特性,分别是:封装、继承、多态。
其中,封装是面向对象的最基本的概念。我们之前提到,对象是一个容器,装的是数据和功能。这本身就是一种封装思想,将数据和功能封装到一个对象中。
对象封装了数据和功能,那对象(类)就有了管理数据和功能的权利,即对象或者类可以把数据和功能给隐藏起来或者开放给使用者,限制对象(类)的使用者权利,或者控制/规范使用者的使用方式。
隐藏属性
如果类的设计者不想使用者直接访问到属性,就可以将属性给隐藏起来。属性分为数据属性和功能属性,即这两类属性都可以被隐藏。
隐藏属性可以隐藏类中的共有属性和对象的私有属性。
# 使用双下划线开头命名的属性将会被隐藏 class foo: __x = 1 # 隐藏类中的共有数据属性 def __init__(self, name, age): self.__name = name # 隐藏对象的私有属性 self.age = age def __f1(self): # 隐藏类内定义的函数属性 print('from test') def f2(self): # f2没有被隐藏,可以被访问到 print(self.__x) print(self.__f1) ############################################################ print(foo.__x) # 访问不到 print(foo.__f1) # 访问不到 print(foo.f2) # 可以访问到 print(_foo.__x, _foo.__f1) # 可以访问 obj = foo('xliu', 18) # 实例化对象obj print(obj.__name) # 访问不到 print(obj.age) # 可以访问到 foo.__y = 2 # 增加类foo的数据属性 print(foo.__y) # 可以访问到
注意点:
# 1 在类外部无法直接访问双下滑线开头的属性,在类内部可以访问到 # 2 在类定义阶段,双下划先开头的属性会发生变形,变为 _foo__x, _foo__f1, _foo__name, 所以在在类外无法直接通过过 .__x 的方式访问。但是可以通过变形后的 _foo__x访问。但这是没有意义的。所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。 # 3 之所以在类内部可以直接通过__x 访问,是因为__开头的属性会在检查类体代码语法时统一发生变形(类定义阶段) # 4 这种变形操作只在检查类体语法的时候发生一次,之后再定义的__开头的属性都不会变形,所以可以直接__y访问到
开放接口
定义属性的目的是为了使用,所以隐藏属性的目的不是单纯的隐藏,而是为了更好的使用。
想要这些属性被使用,就必须提供给使用者一些接口,即没有被隐藏属性。
隐藏数据属性:将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制。
class student: def __init__(self, name): self.__name = name def get_name(self): # 通过该接口就可以间接地访问到名字属性 print(self.__name) def set_name(self, new_name): # 通过改接口判断用户修改的新名字是否合法;非法则修改,不合法就不修改 if type(new_name) is not str: print('小垃圾,名字必须是字符串类型') return self.__name = new_name
隐藏函数/方法属性:目的是为了隔离复杂度。例如atm程序的取款功能,该功能有很多其他功能组成。比如插卡、身份认证、输入金额、打印小票、取钱等,而对使用者来说只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来。
>>> 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() ... >>> obj=atm() >>> obj.withdraw()
总结隐藏属性与开放接口:本质就是为了明确地区分内外,类内部可以修改封装内的东西而不影响外部调用者的代码;而类外部只需拿到一个接口,只要接口名、参数不变,则无论设计者如何改变内部实现代码,使用者均无需改变代码。这就提供一个良好的合作基础,只要接口这个基础约定不变,则代码的修改不足为虑。
上一篇: go语言系列-从数组到map
下一篇: 唐太宗为什么要雪藏尉迟敬德?原因是什么