7.2.4 再谈隐藏
默认情况下,可从外部访问对象的属性。再来看一下前面讨论封装时使用的示例。
>>> 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)来访问。
注意 第9章将介绍特性(property),这是一种功能强大的存取器替代品。
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 *不会导入以一个下划线打头的名称。
领取专属 10元无门槛券
私享最新 技术干货