📄 一句话概括
隐藏不是不让人看,是规定只能从哪儿进——实测 ac.__pin 拿不到,ac._Account__pin 能拿到。
📝 本节和前面几节的关系
4.4 把东西装进盒子,这一节讲盒子上开哪几个口、其余的怎么堵。
一、两个下划线和一个下划线的差别
Python 没有 private 关键字,只有下划线约定。
一个下划线:self._balance——约定私有,靠自觉。
两个下划线:self.__pin——解释器把名字改写成 _Account__pin,真挡住了。
但改写不是加密:知道规则的人照样能拿到。所以区别是【要不要动手绕过】。
二、实测三条
ac._balance → 能(靠自觉)
ac.__pin → 不能(已改名)
ac._Account__pin → 能
所以这叫【挡】,不叫【封死】——它是明确的告示,不是保险箱。
三、真正的价值在改得动
字段藏起来之后,你可以换它的实现,而所有调用方都不知道。
比如把余额从整数改成【分】为单位,只要 add/value 两个口的签名不变,外面一行都不用改。
这就是【内部怎么摆,外面管不着】——第 5 章讲内存结构的前提。
没有隐藏,任何一次内部调整都会变成一场全项目排查。
四、边界:隐藏不是什么
不是安全机制。Python 的改写防得住手滑,防不住恶意。 不是越多越好。全藏起来、一个口都不开,这个类就成了黑盒,没人能用。 判断标准:外面真正需要的那几件事,各给一个口;其余全藏。 口子越少,将来改起来越自由——这是反直觉但最实用的一条。
五、动手 Demo:类的功能之二——隐藏
一个下划线和一个下划线,到底差多少。 造一个实例,挨个试三种访问方式。
class Account:
def __init__(self, balance):
self._balance = balance # 约定私有
self.__pin = "1234" # 名字改写
ac = Account(100)
print(hasattr(ac, "_balance"))
print(hasattr(ac, "__pin"))
print(hasattr(ac, "_Account__pin"))
实跑输出:
--------------------------------------------------------------------
类的功能之二——隐藏
--------------------------------------------------------------------
【隐藏】不是不让人看,**是规定只能从哪儿进。**
Python 没有 `private` 关键字,只有下划线约定——**但它真的挡得住:**
self._balance 一个下划线=约定私有,靠自觉
self.__pin 两个下划线=解释器改名,真挡住了
访问方式 能拿到吗
------------------------------------
ac._balance 能(靠自觉)
ac.__pin 不能(已改名)
ac._Account__pin 能
**实测:`ac.__pin` 拿不到,但 `ac._Account__pin` 能拿到。**
所以这叫【**挡**】不叫【**封死**】——**它是明确的告示,不是保险箱。**
**隐藏真正的作用在【改得动】上:**
字段被藏起来之后,**你可以换它的实现,而所有调用方都不知道。**
这是第 5 章讲【内存里怎么摆】的前提——**内部怎么摆,外面管不着。**
实测:_balance 能拿到、__pin 拿不到、_Account__pin 能拿到。
所以这叫【挡】不叫【封死】——它是明确的告示,不是保险箱;真正的价值在于内部实现可以换。
30 秒实验
- 找一个你直接改别人对象字段的地方,看那里该不该有方法。
- 把一个类的字段全改成私有,看还缺哪几个口。
常见误区
- 以为两个下划线就是加密。它只是改名,知道规则照样能拿到。
- 以为隐藏得越多越好。一个口都不开的类,没人能用。
自测三问(点开对答案)
1. 一个下划线和两个下划线差在哪? —— 一个只是约定,靠自觉;两个会被解释器改写成 `_类名__属性`,绕不过去。 2. 隐藏的真正价值是什么? —— 能改得动:字段藏起来之后可以换实现,所有调用方都不知道。 3. 隐藏得越多越好吗? —— 不是。外面真正需要的那几件事各给一个口,其余全藏;口子越少,将来改起来越自由。- ☐ 下一节(4.6 类的功能之三:创建很多个)
相关笔记
- 类的功能之三:创建很多个 — 最后一个口
- 三大要素之三:去除类的重复定义的继承 — 藏起来的东西怎么被共享
- 明确定义为编程结构 — 结构层面的封装
- 🧩 面向对象是怎样工作的 · 索引