封装

面向对象程序设计(Object-oriented programming,缩写 OOP)是指一种程序设计范型,它强调一切行为都是基于对象(object)完成的,而对象则指的是(class)的实例。对象被作为程序的基本单元,数据和行为方法封装在其中,以提高软件的重用性、灵活性和扩展性,对象的行为方法可以访问和修改对象的数据。通过对象之间的相互协作,完成复杂的程序功能。面向对象编程语言具备封装、抽象、继承、多态等特性。

封装,又称信息隐藏,是指利用抽象数据类型(ADT)将数据和基于数据的操作封装在一起,尽可能地隐藏内部细节,只暴露一些公共接口与外部发生交互。面向对象编程语言使用类进行封装,数据和基于数据的操作对应于类的属性和方法。

具备封装性的面向对象程序设计隐藏了方法的具体执行步骤,取而代之的是对象之间的消息传递。举个例子,假设一个“歌唱家”想要“唱歌”,她当然知道自己该如何发声,但其他人没有必要了解她发声的细节,只管欣赏她美妙的歌喉。

/* 一个面向过程的程序会这样写: */
定义莱丝
莱丝.设置音调(5)
莱丝.吸气()
莱丝.吐气()

/* 当唱歌方法被封装到类中,任何歌唱家都可以简单地使用: */
定义歌唱家类
声明莱丝是一个歌唱家
莱丝.唱歌()

访问限制

使用封装能够对成员属性和方法进行精确的访问控制,通常来说,成员会依照它们的访问权限被分为3种:公有成员、私有成员以及保护成员,保护成员是指可以被子类访问的成员。有的语言更进一步:Java 专门提供了 public、private、protected 和缺省四个级别的访问权限控制关键字。Python 则更提倡开放,尽管没有强制要求,但也建议程序员使用带有下划线的命名风格来规范属性和方法的访问权限。

在 Python 中,非下划线开头的属性称为公有属性,单下划线或双下划线开头的属性称为私有属性,双下划线开头的私有属性不会被子类可见,Python 社区很少提及受保护的属性。PEP 8 提倡对于非公有方法和属性使用单个下划线开头,只有在避免子类命名冲突时才采用双下划线开头,这是因为解释器会改写双下划线开头的属性,改写为类名 + 变量名的格式。比如下面代码中的 __v3 就被改写为 _C__v3

>>> class C:
...     v1 = 1
...     _v2 = 2
...     __v3 = 3
... 
>>> [_ for _ in dir(C) if 'v' in _]
['_C__v3', '_v2', 'v1']

即便如此,Python 也不能严格保证私有属性不能被外部访问。子类之所以不能访问父类的双下划线开头的属性,只是因为改写后的属性名称不相符而已。

对于私有属性,《Effective Python》也建议尽量少用双下划线开头的属性,宁可让子类更多地访问父类的单下划线开头的私有属性,也不要使用双下划线命名限制子类访问,并在文档中把这些属性的合理用法告知子类的开发者。

为什么 Python 不从语法上严格保证私有属性的私密性呢?因为 Python 社区认为开放要比封闭好。而且,Python 提供了一些操作属性的特殊方法,如 __getattr__,使得无法隔绝私有属性的访问,既然如此,那么就默认开发者遵循 Python 编码风格和规范,能够按需操作类内部的属性。

Last updated

Was this helpful?