python基础(面向对象(封装、私有化封装))

封装
封装(encapsulation)指的是向外部隐藏不必要的细节。这听起来有点像多态(无需知道对象的内部细节就可使用它)。这两个概念很像,因为它们都是抽象的原则。它们都像函数一样,可帮助你处理程序的组成部分,让你无需关心不必要的细节。
但封装不同于多态。多态让你无需知道对象所属的类(对象的类型)就能调用其方法,而封装让你无需知道对象的构造就能使用它。听起来还是有点像?下面来看一个使用了多态但没有使用封装的示例。假设你有一个名为OpenObject的类
>>> o = OpenObject() # 对象就是这样创建的
>>> o.set_name('Sir Lancelot') 
>>> o.get_name() 
'Sir Lancelot'
你(通过像调用函数一样调用类)创建一个对象,并将其关联到变量o,然后就可以使用方法set_name和get_name了(假设OpenObject支持这些方法)。一切都看起来完美无缺。然而,如果o将其名称存储在全局变量global_name中呢?
>>> global_name 
'Sir Lancelot' 
这意味着使用OpenObject类的实例(对象)时,你需要考虑global_name的内容。事实上,必须确保无人能修改它。
>>> global_name = 'Sir Gumby' 
>>> o.get_name() 
'Sir Gumby' 
如果尝试创建多个OpenObject对象,将出现问题,因为它们共用同一个变量。
>>> o1 = OpenObject() 
>>> o2 = OpenObject() 
>>> o1.set_name('Robin Hood') 
>>> o2.get_name() 
'Robin Hood' 
如你所见,设置一个对象的名称时,将自动设置另一个对象的名称。这可不是你想要的结果。
基本上,你希望对象是抽象的:当调用方法时,无需操心其他的事情,如避免干扰全局变量。如何将名称“封装”在对象中呢?没问题,将其作为一个属性即可。
属性是归属于对象的变量,就像方法一样。实际上,方法差不多就是与函数相关联的属性。如果你使用属性而非全局变量重新编写前面的类,并将其重命名为ClosedObject,就可像下面这样使用它:
>>> c = ClosedObject() 
>>> c.set_name('Sir Lancelot') 
>>> c.get_name() 
'Sir Lancelot' 
到目前为止一切顺利,但这并不能证明名称不是存储在全局变量中的。下面再来创建一个对象。
>>> r = ClosedObject() 
>>> r.set_name('Sir Robin') 
r.get_name()
'Sir Robin' 
从中可知正确地设置了新对象的名称(这可能在你的意料之中),但第一个对象现在怎么样了呢?
>>> c.get_name() 
'Sir Lancelot' 
其名称还在!因为这个对象有自己的状态。对象的状态由其属性(如名称)描述。对象的方法可能修改这些属性,因此对象将一系列函数(方法)组合起来,并赋予它们访问一些变量(属性)的权限,而属性可用于在两次函数调用之间存储值。

 

私有化封装

默认情况下,可从外部访问对象的属性。再来看一下前面讨论封装时使用的示例。
>>> c.name 
'Sir Lancelot' 
>>> c.name = 'Sir Gumby' 
>>> c.get_name() 
'Sir Gumby' 
有些程序员认为这没问题,但有些程序员(如Smalltalk之父)认为这违反了封装原则。他们认为应该对外部完全隐藏对象的状态(即不能从外部访问它们)。你可能会问,为何他们的立场如此极端?由每个对象管理自己的属性还不够吗?为何要向外部隐藏属性?毕竟,如果能直接访问ClosedObject(对象c所属的类)的属性name,就不需要创建方法setName和getName了。
关键是其他程序员可能不知道(也不应知道)对象内部发生的情况。例如,ClosedObject可能在对象修改其名称时向管理员发送电子邮件。这种功能可能包含在方法set_name中。但如果直接设置c.name,结果将如何呢?什么都不会发生——根本不会发送电子邮件。为避免这类问题,可将属性定义为私有。私有属性不能从对象外部访问,而只能通过存取器方法(如get_name和
set_name)来访问。

Python没有为私有属性提供直接的支持,而是要求程序员知道在什么情况下从外部修改属性是安全的。毕竟,你必须在知道如何使用对象之后才能使用它。然而,通过玩点小花招,可获得类似于私有属性的效果。
要让方法或属性成为私有的(不能从外部访问),只需让其名称以两个下划线打头即可。

class Secretive: 
    def __inaccessible(self): 
         print("Bet you can't see me ...") 
    def accessible(self): 
         print("The secret message is:") 
         self.__inaccessible() 
现在从外部不能访问__inaccessible,但在类中(如accessible中)依然可以使用它。
>>> s = Secretive() 
>>> s.__inaccessible() 
Traceback (most recent call last): 
 File "", line 1, in  
AttributeError: Secretive instance has no attribute '__inaccessible' 
>>> s.accessible() 
The secret message is: 
Bet you can't see me ... 
虽然以两个下划线打头有点怪异,但这样的方法类似于其他语言中的标准私有方法。然而,幕后的处理手法并不标准:在类定义中,对所有以两个下划线打头的名称都进行转换,即在开头加上一个下划线和类名。
>>> Secretive._Secretive__inaccessible 
 
只要知道这种幕后处理手法,就能从类外访问私有方法,然而不应这样做。
>>> s._Secretive__inaccessible() 
Bet you can't see me ... 
总之,你无法禁止别人访问对象的私有方法和属性,但这种名称修改方式发出了强烈的信号,让他们不要这样做。
如果你不希望名称被修改,又想发出不要从外部修改属性或方法的信号,可用一个下划线打头。这虽然只是一种约定,但也有些作用。例如,from module import *不会导入以一个下划线打头的名称。例如,Java支持4种不同的私有程度。Python没有提供这样的支持,不过从某种程度上说,以一个和两个下划线打头相当于两种不同的私有程度。

总结:

第一个层面的封装:类就是麻袋,这本身就是一种封装

第二个层面的封装:类中定义私有的,只有类的内部使用,外部无法访问

第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓

并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)

 

封装的好处

python基础(面向对象(封装、私有化封装))_第1张图片

 

python基础(面向对象(封装、私有化封装))_第2张图片

好处

  1. 在使用面向过程编程时,当需要对数据处理时,需要考虑用哪个模板中哪个函数来进行操作,但是当用面向对象编程时,因为已经将数据存储到了这个独立的空间中,这个独立的空间(即对象)中通过一个特殊的变量(__class__)能够获取到类(模板),而且这个类中的方法是有一定数量的,与此类无关的将不会出现在本类中,因此需要对数据处理时,可以很快速的定位到需要的方法是谁 这样更方便
  2. 全局变量是只能有1份的,多很多个函数需要多个备份时,往往需要利用其它的变量来进行储存;而通过封装 会将用来存储数据的这个变量 变为了对象中的一个“全局”变量,只要对象不一样那么这个变量就可以再有1份,所以这样更方便
  3. 代码划分更清晰

你可能感兴趣的:(python基础(三))