7.3 【基础】私有变量与私有方法 ============================== 1. 下划线妙用 ------------- 在 Python 中,下划线可是非常推荐使用的符号: 1. 变量名推荐使用下划线分隔的蛇形命名法 2. 魔法方法、构造函数都需要使用双下划线 3. 对于暂时用不到的变量值,可以赋值给单下划线 ``_`` 进行占位 根据分类,我把下划线写法分成下面五种: - 单前导下划线:\ ``_var`` - 单末尾下划线:\ ``var_`` - 双前导下划线:\ ``__var`` - 双前导和末尾下划线:\ ``__var__`` - 单下划线:\ ``_`` 由于篇幅所限,本篇将只介绍跟标题(私有变量与私有方法)有关的用法,也就是访问控制。 上面五种写法中,涉及到访问控制的有:\ ``_var`` 和 ``__var`` 2. 单前导下划线 \_var --------------------- 下划线前缀的含义是告知其他程序员:\ **以单个下划线开头的变量或方法仅供内部使用**\ 。 请看下面这个例子 .. code:: python class Demo: def __init__(self): self.foo = 11 self._bar = 22 如果你实例化此类,然后分别访问 ``self.foo`` 和 ``self._bar`` 会发生什么情况? .. code:: python >>> demo = Demo() >>> demo.foo 11 >>> demo._bar 22 结果是:外界都可以直接访问这两个属性。 但实际上,二者是有区别的。PEP 8 有提及,如果一个属性的有单前导下划线,则该属性应该仅供内部访问。 但这并不是强制性的,不然上面我们也不可能通过 ``self._bar`` 访问到 22,但做为一名 Python 程序员最好遵守这一共识。 3. 双前导下划线 \__var ---------------------- 双下划线前缀会导致Python解释器重写属性名称,以避免子类中的命名冲突。 这也叫做\ **名称修饰(name mangling)** - 解释器更改变量的名称,以便在类被扩展的时候不容易产生冲突。 我知道这听起来很抽象。因此,我组合了一个小小的代码示例来予以说明: .. code:: python class Demo: def __init__(self): self.foo = 11 self._bar = 22 self.__baz = 33 将其进行实例化,然后使用 ``dir()`` 函数查看这个对象的属性 .. code:: python >>> demo = Demo() >>> dir(demo) ['_Demo__baz', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_bar', 'foo'] 不难发现,\ ``foo`` 和 ``_bar`` 都很正常,可以使用 ``demo.属性名`` 进行访问。 但 ``__baz`` 明显和 ``foo`` 、 ``_bar`` 不一样,尝试访问后却报了 AttributeError,属性不存在。 .. code:: python >>> demo.__baz Traceback (most recent call last): File "", line 1, in AttributeError: 'Demo' object has no attribute '__baz' 如果你仔细观察,你会看到此对象上有一个名为\ ``_Demo__baz``\ 的属性。这就是Python解释器所做的名称修饰。它这样做是为了防止变量在子类中被重写。 如果想访问,那得按照 dir 提示的写法去访问,在 ``__baz`` 前面加上 ``_类名``\ 。 .. code:: python >>> demo._Demo__baz 33 总结可得,使用双下划线开头的属性变量,就是一个私有变量。 这样的规则在属性上生效,在方法上也同样适用。 如果一个实例方法,以双下划线开头,那么这个方法就是一个私有的方法,不能由实例对象或者类直接调用。 必须得通过 ``实例._类名__方法名`` 来调用。 4. 总结一下 ----------- Python并没有真正的私有化支持,但可用下划线得到伪私有。 尽量避免定义以下划线开头的变量。 - 私有变量:以双下划线前导的变量,可以使用 ``实例._类名__变量名`` 进行访问 - 私有方法:以双下划线前导的方法,可以使用 ``实例._类名__方法名()`` 进行访问 私有变量和私有方法,虽然有办法访问,但是仍然不建议使用上面给出的方法直接访问,而应该接口统一的接口(函数入口)来对私有变量进行查看、变量,对私有方法进行调用。对于这些内容我放到了下一节的的封装,请继续往后学习。