⌨️ 第五章 从设计到测试
📄 本篇摘要

封装=把数据和操作装进类里,只对外公开必要的接口,内部细节藏起来。工具是访问修饰符:public(公开)、private(私有)、protected(家族可见)。Python 没有真正的私有——全靠下划线约定:约定即法律。

📝 关系

上一节:字段、方法与属性 | 下一节:多态与接口

一、封装的动机:藏起来才安全

把内部字段公开,等于把遥控器拆开让用户直接摸电路板——今天改个内部变量名,明天全世界的调用代码一起崩。封装的承诺是:外部只依赖公开接口,内部实现随时可换(5-17 的 property 是它的前哨)。

二、访问修饰符三档

修饰符 谁能访问 用途
public 任何人 对外接口
protected 本类+子类 家族内部共享
private 仅本类 纯内部实现

C++/Java 是语言的硬规则:private 成员外面真访问不了,编译器当场拦。Python 走约定路线:

Python 的态度:我们是成年人——封装靠自觉与工具(linter 会揪出越界访问),不靠语言看守。

三、封装的正确姿势

不是【藏得越多越好】,而是接口设计:

  1. 先问哪些是接口:调用方真正需要什么操作?(账户需要 deposit/withdraw/balance,不需要 raw_amount);
  2. 其余一律藏:字段私有+property 暴露(5-17);
  3. 不变的约定写进接口名:接口一旦公开就是承诺,改名成本极高——公开前多想一分钟。

四、动手 Demo:同一个账户类的三道门

class Account:
    def __init__(self, owner, balance):
        self.owner = owner            # 公开:谁都能看
        self._balance = balance       # 内部:下划线约定
        self.__pin = "1234"           # 改写名:语言层拦截

    @property
    def balance(self):
        return self._balance

    def withdraw(self, amount, pin):
        if pin != self.__pin:
            return "密码错误"
        if amount > self._balance:
            return "余额不足"
        self._balance -= amount
        return f"取出 {amount},余 {self._balance}"

acct = Account("波仔", 100)
print(acct.owner, acct.balance)          # 走正门
print(acct.withdraw(30, "1234"))
print(acct.withdraw(9999, "1234"))       # 业务规则把关
try:
    print(acct.__pin)                     # 想摸电路板
except AttributeError as e:
    print("拦截:", type(e).__name__)
print(acct._balance)                      # 单下划线:约定不拦,但这是违规操作

沙箱实跑输出:

波仔 100
取出 30,余 70
余额不足
拦截: AttributeError
70

看到什么算成功:正门操作正常、业务规则生效;__pin 被语言拦截;_balance 摸得到但那是违规。

读结果:三种可见性三道门——公开接口(正门)、约定内部(员工通道)、改名拦截(保险柜)。注意 withdraw 里有两条业务规则:封装不只是藏字段,更是让规则只写在一个地方——没有封装的话,【检查密码、检查余额】得散落在每个调用处。

五、30 秒实验

打印 acct._Account__pin——拿到 "1234"。双下划线不是保险柜,是提醒牌:Python 相信你读过这条注释后知难而退。真要保密,数据加密/权限系统才是正解(数据库课 6-8 的密码哈希同理)。

六、常见误区

误区 真相
Python 的 __ 是真私有 名称改写(name mangling)可绕过(_类名__名);本质是强约定
全部藏起来最安全 接口太少类没法用;封装是【设计接口】,不是【一味隐藏】
protected 在 Python 里存在 Python 只有约定(_);protected 是 C++/Java 的语言级概念
自测三问(点击展开) 1. **三种下划线前缀各什么含义?** 答:无=公开;`_`=内部约定(不拦);`__`=名称改写(语言层报错但可绕)。 2. **封装除了藏字段还藏什么?** 答:业务规则——让规则集中一处(如 withdraw 内),不散落调用方。 3. **接口为什么公开前要慎之又慎?** 答:公开即承诺,后期改名的成本随调用方数量暴涨。

相关笔记