与C++和Java不同的是,对Python类来说,其属性的可见度只有两种,也就是public和private。
class MyObject(object):
def __init__(self):
self.public_field = 5
self.__private_field = 10
def get_private_field(self):
return self.__private_field
任何人都可以在对象上通过dot操作符(即.操作符)来访问类的public属性。
foo = MyObject()
assert foo.public_field == 5
由两个下划线开头,且结尾最多有一个下划线(或没有)的属性,是private字段。本类的所有方法可以直接访问它们。
assert foo.get_private_field() == 10
在类的外面直接访问它的private字段会引发异常。
foo.__private_field
>>>
AttributeError: 'MyObject' object has no attribute '__private_field'
由于类的class method仍然声明在本类的class代码块之内,所以这些方法也是可以直接访问类的private属性的。
class MyOtherObject(object):
def __init__(self):
self.__private_field = 71
@classmethod
def get_private_field_of_instance(cls, instance):
return instance.__private_field
bar = MyOtherObject()
assert MyOtherObject.get_private_field_of_instance(bar) == 71
和C++/Java一样,Python中子类无法访问父类的private字段。
class MyParentObject(object):
def __init__(self):
self.__private_field = 71
class MyChildObject(MyParentObject):
def get_private_field(self):
return self.__private_field
baz = MyChildObject()
baz.get_private_field()
>>>
AttributeError: 'MyChildObject' object has no attribute
'_MyChildObject__private_field'
这是因为Python会对私有属性的名称(以两个下划线开头的属性名称)做一些简单的变换,以保证"private"字段的私密性。当编译器看到MyChildObject.get_private_field()方法要访问私有属性时,它会先把__private_field变换为_MyChildObject__private_field,然后再进行访问。本例中,__private_field字段只在MyParentObject.__init__()里面做了定义,因此这个私有属性的真实名称,实际上是_MyParentObject__private_field(保存在baz.__dict__中)。
print(baz.__dict__)
assert baz._MyParentObject__private_field == 71
>>>
{'_MyParentObject__private_field': 71}
Python为什么不从语法上严格保证private字段的私密性呢?用最简单的话来说,就是:We are all consenting adults here. 这句广为流传的格言,表达了很多Python程序员的观点,即开放要比封闭好。
另外一个原因在于:Python语言本身就已经提供了一些属性挂钩(例如__getattr__, __getattribute__和__setattr__),使得开发者能够按照自己的需要任意操纵对象内部的数据。既然如此,那为什么还要阻止访问private属性呢?
为了尽量减少无意间访问内部属性所带来的意外,Python程序员会遵照《Python风格指南》的建议,用一种习惯性的命名方式来表示这种字段。也就是说,以单个下划线开头的字段,应该视为"protected"字段,本类之外的那些代码在使用这种字段时要多加小心。
虽说有了这种约定,仍然有许多Python编程新手会使用私有字段(两个下划线开头)表示那种不应该由子类或外部来访问的API:
class MyClass(object):
def __init__(self, value):
self.__value = value
def get_value(self):
return str(self.__value)
foo = MyClass(5)
assert foo.get_value() == '5'
这种写法不好。因为包括你自己在内的许多开发者,以后可能都需要从这个类中继承子类,并在子类中添加新的行为,或是改进现有方法中效率不高的那些部分(例如,MyClass.get_value()方法总是返回字符串,这可能就需要改进)。假如超类使用了"private"属性,那么子类在覆写或扩展时,就会显得既笨拙又容易出错。其实那些潜在的子类在迫不得已时,仍然能够访问那些"private"字段:
class MyIntegerSubclass(MyClass):
def get_value(self):
return int(self._MyClass__value)
foo = MyIntegerSubclass(5)
assert foo.get_value() == 5
上面的代码写法缺陷很明显,一旦MyClass增加了一个父类并将self.__value的定义移到MyClass的父类中,MyIntegerSubclass中的绝对引用就会失效。