← 返回 Day 36 正文

Day 36 · 图解

Day 036 — 封装与数据隐藏:图解

Mermaid 与 ASCII 示意图,帮助理解封装机制、名称改写、Property 和描述符


1️⃣ 封装的概念

flowchart TB subgraph 封装类 I[接口层] I --> M[公开方法] I --> P[公开属性] end subgraph 内部实现 D[私有数据] L[内部逻辑] S[状态管理] end M -.->|控制访问| D M -.->|调用| L P -.->|读取/验证| D style I fill:#90EE90 style D fill:#FFB6C1
flowchart TB
    subgraph 封装类
        I[接口层]
        I --> M[公开方法]
        I --> P[公开属性]
    end

    subgraph 内部实现
        D[私有数据]
        L[内部逻辑]
        S[状态管理]
    end

    M -.->|控制访问| D
    M -.->|调用| L
    P -.->|读取/验证| D

    style I fill:#90EE90
    style D fill:#FFB6C1

封装的比喻

🚗 汽车驾驶 vs 引擎内部

    用户看到:                   用户不关心:
    ┌─────────┐                ┌──────────────────┐
    │ 方向盘   │ ← 接口          │ 燃油喷射系统      │
    │ 油门     │                │ 气缸点火时序      │
    │ 刹车     │                │ 变速箱齿轮比      │
    │ 仪表盘   │                │ ECU 控制算法      │
    └─────────┘                │ 排气催化转化      │
                               └──────────────────┘

    用户通过接口操作               内部实现可以随意修改
    接口保持一致                   不影响用户使用

2️⃣ 名称改写(Name Mangling)机制

flowchart LR C["class MyClass"] --> A1["self.__secret = 42"] A1 --> A2["解释器处理:"] A2 --> A3["self._MyClass__secret = 42"] style A3 fill:#FFD700 B1["obj.__secret"] -.->|AttributeError| B2["❌"] B3["obj._MyClass__secret"] -.->|42| B4["✅"]
flowchart LR
    C["class MyClass"] --> A1["self.__secret = 42"]
    A1 --> A2["解释器处理:"]
    A2 --> A3["self._MyClass__secret = 42"]

    style A3 fill:#FFD700

    B1["obj.__secret"] -.->|AttributeError| B2["❌"]
    B3["obj._MyClass__secret"] -.->|42| B4["✅"]

名称改写规则

源代码:                    实际存储:
══════════                ══════════════

public = "公开"            public = "公开"
_protected = "受保护"      _protected = "受保护"
__private = "私有"         _ClassName__private = "私有"
__magic__ = "魔术"         __magic__ = "魔术" (不改写)

继承链中的名称改写

classDiagram class Base { +name: str _internal: str -__secret: str # _Base__secret } class Derived { -__secret: str # _Derived__secret } Base <|-- Derived note for Base: Base 的 __secret 是 _Base__secret note for Derived: Derived 的 __secret 是 _Derived__secret note for Derived: ⚠️ 它们不冲突!
classDiagram
    class Base {
        +name: str
        _internal: str
        -__secret: str  # _Base__secret
    }

    class Derived {
        -__secret: str  # _Derived__secret
    }

    Base <|-- Derived

    note for Base: Base 的 __secret 是 _Base__secret
    note for Derived: Derived 的 __secret 是 _Derived__secret
    note for Derived: ⚠️ 它们不冲突!
实例 d = Derived()

d.__dict__:
    _Base__secret = "Base的秘密"     ← 来自 Base.__init__
    _Derived__secret = "Derived的秘密" ← 来自 Derived.__init__

d.get_base_secret():
    访问 _Base__secret → "Base的秘密"

d.get_derived_secret():
    访问 _Derived__secret → "Derived的秘密"

3️⃣ Python 属性约定

命名约定层级:

                       公开 API
                     ┌──────────┐
                     │  name    │ ← 公共接口的一部分
                     │  price   │    保证稳定
                     │  get()   │
                     └──────────┘

                   内部实现(约定)
                     ┌──────────┐
                     │  _data   │ ← 内部使用
                     │  _cache  │    可以随时修改
                     │  _init() │    子类可以访问
                     └──────────┘

                   名称改写私有
                     ┌──────────┐
                     │  __key   │ ← 避免子类覆盖
                     │  __secret│    更强的私有暗示
                     └──────────┘

                   魔术方法
                     ┌──────────┐
                     │  __init__│ ← Python 内部协议
                     │  __str__ │    不要自己发明
                     └──────────┘

4️⃣ @property 工作流程

flowchart TB AC["obj.attr"] --> Q{有 @property 吗?} Q -->|是| G["调用 getter 方法"] Q -->|否| D["直接访问实例字典"] AS["obj.attr = value"] --> QS{有 setter 吗?} QS -->|是| GS["调用 setter 方法<br/>(可以验证数据)"] QS -->|否| DS["直接写入实例字典"] style G fill:#90EE90 style GS fill:#90EE90 subgraph 验证逻辑 V1["检查类型"] V2["检查范围"] V3["触发事件"] end GS --> V1 GS --> V2 GS --> V3
flowchart TB
    AC["obj.attr"] --> Q{有 @property 吗?}
    Q -->|是| G["调用 getter 方法"]
    Q -->|否| D["直接访问实例字典"]

    AS["obj.attr = value"] --> QS{有 setter 吗?}
    QS -->|是| GS["调用 setter 方法<br/>(可以验证数据)"]
    QS -->|否| DS["直接写入实例字典"]

    style G fill:#90EE90
    style GS fill:#90EE90

    subgraph 验证逻辑
        V1["检查类型"]
        V2["检查范围"]
        V3["触发事件"]
    end

    GS --> V1
    GS --> V2
    GS --> V3

Property vs 直接属性

直接属性访问:                      使用 @property:
═════════════════                  ═══════════════════

class Circle:                      class Circle:
    def __init__(self, r):             def __init__(self, r):
        self.radius = r                    self._radius = r

                                     @property
c = Circle(-5)  # ✅ 可接受负数!     def radius(self):
                                         return self._radius

# 想加验证怎么办?                   @radius.setter
# 改为 set_radius() → 接口变了!     def radius(self, value):
                                         if value <= 0:
                                              raise ValueError(...)
                                     c = Circle(-5)  # ❌ ValueError
                                     c.radius = 10  # ✅ 像属性一样赋值!

5️⃣ 描述符协议

flowchart LR subgraph 描述符协议 GET["__get__(self, obj, type)"] SET["__set__(self, obj, value)"] DEL["__delete__(self, obj)"] NAME["__set_name__(self, owner, name)"] end subgraph 类定义 C["class Order:"] F1["quantity = NonNegative()"] F2["price = NonNegative()"] end subgraph 访问过程 A1["o.quantity"] -->|查找| M["类字典中找到 NonNegative 描述符"] M -->|调用| GET end C --> F1 C --> F2
flowchart LR
    subgraph 描述符协议
        GET["__get__(self, obj, type)"]
        SET["__set__(self, obj, value)"]
        DEL["__delete__(self, obj)"]
        NAME["__set_name__(self, owner, name)"]
    end

    subgraph 类定义
        C["class Order:"]
        F1["quantity = NonNegative()"]
        F2["price = NonNegative()"]
    end

    subgraph 访问过程
        A1["o.quantity"] -->|查找| M["类字典中找到 NonNegative 描述符"]
        M -->|调用| GET
    end

    C --> F1
    C --> F2

描述符访问优先级

obj.attr 查找顺序:
1. 数据描述符(定义了 __set__ 或 __delete__)
2. 实例 __dict__
3. 非数据描述符(只定义了 __get__)
4. 类 __dict__
5. __getattr__(如果定义了)

示例:@property 是数据描述符
     @cached_property 是非数据描述符(只定义 __get__)

6️⃣ 安全 API 客户端架构

classDiagram class APIClient { -__api_key: str -__api_secret: str -__base_url: str -__session: Session -__call_count: int -__failed_count: int #rate_limit: int #timeout: int +base_url: str (property) +api_key: str (property, masked) +call_count: int (property) +success_rate: float (property) +rate_limit: int (property) +get(path) dict +post(path, data) dict +put(path, data) dict +delete(path) dict +close() void +health_check() dict #_request() dict #_log() void #_check_rate_limit() void #_make_signature() str #_get_session() Session } class SecureAPIClient { #users: UsersAPI +users: UsersAPI (property) } class UsersAPI { #client: APIClient +list() dict +get(id) dict +create(name, email) dict +update(id, **kwargs) dict +delete(id) dict } APIClient <|-- SecureAPIClient SecureAPIClient --> UsersAPI : 组合
classDiagram
    class APIClient {
        -__api_key: str
        -__api_secret: str
        -__base_url: str
        -__session: Session
        -__call_count: int
        -__failed_count: int
        #rate_limit: int
        #timeout: int
        +base_url: str (property)
        +api_key: str (property, masked)
        +call_count: int (property)
        +success_rate: float (property)
        +rate_limit: int (property)
        +get(path) dict
        +post(path, data) dict
        +put(path, data) dict
        +delete(path) dict
        +close() void
        +health_check() dict
        #_request() dict
        #_log() void
        #_check_rate_limit() void
        #_make_signature() str
        #_get_session() Session
    }

    class SecureAPIClient {
        #users: UsersAPI
        +users: UsersAPI (property)
    }

    class UsersAPI {
        #client: APIClient
        +list() dict
        +get(id) dict
        +create(name, email) dict
        +update(id, **kwargs) dict
        +delete(id) dict
    }

    APIClient <|-- SecureAPIClient
    SecureAPIClient --> UsersAPI : 组合

7️⃣ 属性控制模式对比

模式                     访问控制    验证逻辑    计算属性    内存开销
══════════════════════    ═══════    ═══════    ═══════    ═══════

直接属性访问              无          无          无         低
self.x

单下划线约定              约定         无          无         低
self._x

@property                完全控制    有          有          中
@property.setter

描述符协议                可复用      可复用      可复用      高
class Validator

__getattr__              动态创建    无          N/A         无缓存
__setattr__              完全拦截

使用建议

需求 推荐方案
简单属性 self.name
内部实现细节 self._internal
需要验证的计算属性 @property + .setter
多个类共用验证逻辑 描述符
适应未来变化 先 self._x + @property 后期添加
避免子类覆盖 __name 名称改写