认为python的私有方法是一种“伪私有”,因为它并不是在语言层面提供支持。而是采用了一种巧妙的方式让不该被看见的“隐藏”起来,但实际上我们是可以通过特殊手段突破其限制。
私有方法
在Java
中,表示私有一般有 private/protected/public
来表示方法或者变量的可见范围。
python中约定,私有属性一般可以用_
或者__
来表示,如__name
, __add()
。但是二者略有差别。
区分_
和__
_
表达了一种受保护的约定
class Cat:
def _method(self):
print('method from Cat')
littleCat = Cat()
littleCat._method();
用一个下划线的时候,按python
的约定是表示私有化。在pycharm
引用littleCat
实例的时候,是不会提示有_method()
方法的。但是如果你强行手动敲上_method
, 在pycharm
中的也会友情提示你“访问了被保护的成员方法”,如下图所示。
而上述代码实际上是可以执行成功的,结果为
method from Cat
结论,_ 只是表达了一种受保护的约定,但是仍然可以调用(但是很反对这么做)
__
是一种伪私有化
首先,明确一下私有方法的两个特点:
- 在类的外部无法被调用;
- 不能被子类所覆盖;
然后,我们来观察下面的代码,并试着执行一下
class Cat:
def __aowu(self):
print('嗷呜~ from Cat')
littleCat = Cat()
littleCat.__aowu()
发现执行上述代码会报错,如下所示
File "...", line 21, in <module>
littleCat.__aowu()
AttributeError: 'Cat' object has no attribute '__aowu'
可以发现,提示了 方法不存在 ,而不是 调用了私有方法 。为什么呢?
原来,python中用到了name mangling
技术,这个技术主要被各大语言用来解决命名冲突问题。
在py中name mangling
的直接作用是:把 _方法名 变成 类名__方法名 。
所以,这就解释了为什么上述代码在调用__aowu()
的时候会报 方法不存在 的异常,因为它已经变成 _Cat__aowu()
了。
那么,若是调用_Cat__aowu()
,应该可以执行成功吧?
class Cat:
def __aowu(self):
print('嗷呜~ from Cat')
littleCat = Cat()
littleCat._Cat__aowu()
结果如下。是可以执行成功的,和预想的一样
method from Cat
到这里,得到的一个重要结论是:
使用双下划线,会把 __方法名 变成 _类名__方法名 。
这时候,在Cat 之外无法调用 \_\_aowu()
,这就满足了私有方法的“无法在定义它的类之外被调用”特点。
在考虑一下,其实会发现,私有方法的第二个特征“不能被子类所覆盖”其实也满足了。来看一下。
class Cat:
def __aowu(self):
print('嗷呜~ from Cat')
def aowu(self):# 用于暴露一个接口给子类去调用Cat的私有方法__aowu()
self.__aowu() # 从Cat继承而来的方法,间接调用父类Cat的的__aowu()
class Dog(Cat):
def __init__(self):
self.__aowu()
self.aowu()
def __aowu(self):
print('嗷呜~ , learn from Cat!')
Dog()
执行结果为
method from Cat
method from Dog, rewrite from Cat!
有一个Dog
类继承了Cat
,Dog
里面也有一个 \_\_aowu()
。根据上面的结论,Dog
的\_\_aowu()
其实是 \_Dog\_\_aowu()
,而不是Cat中的 \_Cat\_\_aowu()
,所以自然不会覆盖。
如果说 __(双下划线) 是为了表达 私有 的概念,那么上面的例子中,Cat的实例littleCat
不能调用私有方法 \_\_aowu()
,其实已经达到了 私有 的效果,只不过它是悄悄把方法名修改了,从而达到 私有 的效果 ,而并不是语言层面上的 私有 ,所以说 这是一种 伪私有化 。
双下划线方法的覆盖
现在你知道了python用了name mangling
,也知道在这是一种“伪私有”,如果你想要强行覆盖私有方法,也不是不行。来看下面的代码
class Cat:
def __aowu(self):
print('嗷呜~ from Cat')
def aowu(self):# 用于暴露一个接口给子类去调用Cat的私有方法__aowu()
self.__aowu()
class Dog(Cat):
def __init__(self):
self.__aowu() # 调用Dog自身__aowu()
self.aowu() # 从Cat继承而来的方法,间接调用父类Cat的的__aowu()
self._Cat__aowu() # 调用Dog自己写的__aowu()
def __aowu(self):
print('嗷呜~ , learn from Cat!')
def _Cat__aowu(self):
print('嗷呜222~ , learn from Cat!')
Dog()
可以看到,如果你在子类Dog中,手动增加一个_Cat__aowu()
方法。如果Dog把父类Cat的__aowu()
给重写了,那么第二行输出的应该是 “嗷呜222~ , learn from Cat!”。
试着执行一下,果不其然。
运行结果为
嗷呜~ , learn from Cat!
嗷呜222~ , learn from Cat!
嗷呜222~ , learn from Cat!
所以,非特殊情况不要给一个方法以 _类名__
开头。一旦取了这种妖魔鬼怪的名字,指不定就把什么私有方法给给覆盖了。
类的专有方法
| 方法 | 释义 |
| ————— | ————- |
| __init__ | 构造函数,在生成对象时调用 |
| __del__ | 析构函数,释放对象时使用 |
| __repr__ | 打印,转换 |
| __setitem__ | 按照索引赋值 |
| __getitem__ | 按照索引获取值 |
| __len__ | 获得长度 |
| __cmp__ | 比较运算 |
| __call__ | 函数调用 |
| __add__ | 加运算 |
| __sub__ | 减运算 |
| __mul__ | 乘运算 |
| __truediv__ | 除运算 |
| __mod__ | 求余运算 |
| __pow__ | 乘方 |