Python 之私有属性
概要
在基类的定义中,如果有些属性或者方法,我们希望隐藏它,从而不被子类继承,或者使其不被实例直接访问到,这时候可以用到私有属性的命名方法。尽管类的所有属性和方法在某种意义上说都是“暴露的”,但是私有属性的存在即表达了我们的态度。
私有属性命名规则
在类中所有以双下划线开头的名称都会自动变形,比如一个私有属性名为 __print
,会自动变形为 _Classname__print
,这样就有效避免了在子类中使用的私有名称不会与基类中使用的相同私有名称发生冲突。示例代码如下:
# -*- coding: utf-8 -*-
class Marvel(object):
def __init__(self):
self.signature = self.__print()
def __print(self): # 私有实例方法,变形为 _Marvel__print
return "I love Marvel movies!"
class EvilMarvel(Marvel):
def __init__(self):
super().__init__()
self.signature = self.__print()
self.base_signature = self.__base_print()
def __print(self): # 私有实例方法,变形为 _DC__print
return "I love DC movies!"
def __base_print(self):
return self._Marvel__print() # 强制调用父类的私有方法
a = Marvel()
print(a.signature) # 输出 I love Marvel movies!
print(a.__print()) # NameError: name '_Marvel__print' is not defined
print(a._Marvel__print()) # 强制访问,输出 I love Marvel movies!
b = EvilMarvel()
print(b.signature) # 输出 I love DC movies!
print(b.base_signature) # 输出 I love Marvel movies!
尽管上述两个类别都有 __print
方法,实际调用时不会发生冲突,因为它们都在本身所属的类别中进行变形。当然,我们也可以强制性调用父类中的私有方法,比如 EvilMarvel 中的方法 __base_print
,或者从外部强制访问私有属性,比如上述代码中倒数第五行,虽然语法上没任何问题,但是不建议这么做,这种做法也背离了设置私有属性的初衷。
单下划线在模块中的作用
单下划线在模块方法中也表示“私有”定义的命名,但它是防止通过 from moddule import *
语句导出名称,比如我们将上个示例代码中子类 EvilMarvel 前加一个下划线,并将文件名命名为 Marvel,再新建一个包含如下代码的文件,调用 _EvilMarvel
时会发生错误,
from Marvel import *
a = _EvilMarvel()
print(a.signature) # NameError: name '_EvilMarvel' is not defined
注意如果显示地引用单下划线开头的方法,则是可以的,比如
from Marvel import _EvilMarvel
a = _EvilMarvel()
print(a.signature) # 输出 I love DC movies!
另外可以通过定义模块中的列表 __all__
,可以精确控制 from moddule import *
导入的名称集合,不过在此不作过多说明。
值得一提的是,这种单下划线的方式在类中,与普通属性名没有任何区别,不要混淆。
总结
Python 中的私有属性是一种通过添加双下划线的“伪”方法,这样可以避免父类与子类定义的对象之间发生命名空间冲突和暴露对象的内部实现,以及防止被实例对象直接访问得到,从而增强代码的健壮性。