前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开源图书《Python完全自学教程》8.7.1下划线

开源图书《Python完全自学教程》8.7.1下划线

作者头像
老齐
发布2022-07-06 16:15:10
5540
发布2022-07-06 16:15:10
举报
文章被收录于专栏:老齐教室老齐教室

8.7 封装和私有化

在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,将某些部分“隐藏”起来,在程序外部“看不到”,其含义是其他程序无法调用,不是人用眼睛看不到那个代码。如果让代码变成人难以阅读和理解的形式,这种行为称作“代码混淆”(obfuscation)。

8.7.1 下划线

Python 中的下划线是一种含义很丰富的符号。

此前的内容中,已经使用过下划线( _ ),比如变量名称如果是由两个单词构成,中间用下划线连接;再比如类的初始化方法 __init__() 是以双下划线开始和结束。现在探讨对象的封装,也可以用下划线实现,方式非常简单,即在准备封装的对象名字前面加“双下划线”。例如:

代码语言:javascript
复制
>>> class Foo:
...     __name = "laoqi"    
...     book = 'python'
...
>>> f = Foo()
>>> f.book
'python'
>>> f.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__name'

在类 Foo 中有两个类属性,__name 是用双下划线开始命名的类属性;book 是通常见到的类属性命名。

创建实例 ff.book 能正确地显示属性的值;但是,f.__name 则显示了 AttributeError 异常。这说明在类 Foo 之外,无法调用 __name 属性。

代码语言:javascript
复制
>>> Foo.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute '__name'
>>> hasattr(Foo, "__name")
False
>>> hasattr(Foo, "book")
True

除了用实例无法调用 __name 属性,用类名称 Foo 也无法调用。在类的外部检测 Foo 类是否具有 __name 属性时,返回了 False ,而检测 book 属性,则返回了 True 。与 book 相比,__name 就被“隐藏”了起来,不论是通过实例名称还是类名称,都无法调用它。

代码语言:javascript
复制
>>> class Foo:
...     __name = "laoqi"
...     book = 'python'
...     def get_name(self):
...         return Foo.__name
...

再给类 Foo 增加一个方法 get_name ,在这个方法中,通过类名称调用 __name 属性。

代码语言:javascript
复制
>>> f = Foo()
>>> f.get_name()
'laoqi'

再次实例化之后,执行 f.get_name() 后返回了类属性 __name 的值,但此属性是在类内部的方法中被调用的。

在 Python 中以双下划线开始命名的属性或方法,都会像 __name 那样,只能在类内部调用,在外部无法调用。将这种行为称为私有化(Private),亦即实现了对该名称所引用对象的封装。

下面的代码是一个比较完整的示例,请读者认真阅读,并体会“私有化”的作用效果。

代码语言:javascript
复制
# coding=utf-8
'''
filename: private.py
'''
class ProtectMe: 
    def __init__(self):
        self.me = "qiwsir"
        self.__name = "laoqi"

    def __python(self):  
        print("I love Python.")

    def code(self):
        print("What language do you like?")
        self.__python()   

if __name__ == "__main__": 
    p = ProtectMe()
    p.code()
    print(p.me)
    p.__python()  

执行程序,看看效果:

代码语言:javascript
复制
% python private.py
What language do you like?
I love Python.
qiwsir
Traceback (most recent call last):
  File "/Users/qiwsir/Documents/my_books/codes/private.py", line 21, in <module>
    p.__python()  
AttributeError: 'ProtectMe' object has no attribute '__python'

执行到 p.__python() 时报 AttributeError 异常,说明方法 __python() 不能调用,因为它的名称用双下划线开始,表明是一个私有化的方法。在 code() 方法内,调用了 __python() 方法,在执行 p.code() 时得到了正确结果,再次表明被封装的对象只能在类的内部调用。

那么,为什么在命名属性或方法时,以双下划线开始就能实现封装呢?其原因在于,Python 解释器会对以这种形式命名的对象重命名,在原来的名称前面增加前缀形如 _ClassName 的前缀。以在交互模式中创建的 Foo 类为例:

代码语言:javascript
复制
>>> Foo.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute '__name'
>>> Foo._Foo__name        # (1)
'laoqi'

Foo 的类属性 __name 被封装,其实是被 Python 解释器重命名为 _Foo__nameFoo 前面是单下划线),若改用注释(1)形式,就可以得到 Foo 类的私有化类属性 __name 的值。

若在 Foo 类内调用 __name ,相当于正在写类内部代码块,类对象尚未经 Python 解释器编译。当类的代码块都编写完毕,Python 解释器将其中所有的 __name 都更名为 _Foo__name ,即可顺利调用其引用的对象。

而在类外面执行 Foo.__name 时,Python 解释器没有也不会将 __name 解析为 _Foo__name ,所以在调用__name 时就显示 AttributeError

读者在这里又看到了另外一种符号:单下划线。

在有的 Python 资料中,并不将上述的方式称为“私有化”——本质是改个名称嘛。而是用单下划线,“约定”该名称引用的对象作为私有化对象——注意是“约定”。

代码语言:javascript
复制
>>> class Bar:
...     _name = "laoqi"
...     def get_name(self):
...         return self._name
...

这里约定 _name 只在类内部调用。诚然,如果你不履约,施行“霸权主义”,Python 也不惩戒该行为——没有抛出异常。

代码语言:javascript
复制
>>> Bar._name
'laoqi'

因此,也有的开发者认为 Python 并不支持真正的私有化,不能强制某对象私有化。于是将“单下划线”视为该对象宜作为内部使用的标记符。

以上两种私有化的观点和写法,此处并不强制读者采信哪一种,因为这都是编程的形式——不用这些也能写程序。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 8.7 封装和私有化
    • 8.7.1 下划线
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档