认为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中的也会友情提示你“访问了被保护的成员方法”,如下图所示。

pycharm访问被保护方法提示

而上述代码实际上是可以执行成功的,结果为

method from Cat

结论,_ 只是表达了一种受保护的约定,但是仍然可以调用(但是很反对这么做)

__是一种伪私有化

首先,明确一下私有方法的两个特点:

  1. 在类的外部无法被调用;
  2. 不能被子类所覆盖;

然后,我们来观察下面的代码,并试着执行一下

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类继承了CatDog里面也有一个 \_\_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__ | 乘方 |