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

代码多了必须分区管理:命名空间防重名(同名各归各家);模块=一个文件(Python 的 .py);软件包=一个目录(模块的文件夹)。三层由小到大:名字不冲突 → 文件可复用 → 目录成体系。

📝 关系

上一节:对象之间的关系 | 下一节:AOP 与 DI

一、命名空间:同名不打架

几百个类里【User】出现三次怎么办?命名空间给名字划地盘:auth.User、blog.User、shop.User 互不干扰。C++ 有 namespace 关键字(C++ 10.6 讲过);Python 天生按模块分命名空间——每个 .py 文件就是一个独立空间,模块名.名字 即跨空间访问。

二、模块:一个文件

模块(module)=一个 .py 文件,里面可以有函数、类、变量。三种 import(树莓派课 6-5 讲过):

import json                      # 整体引入,用 json.dumps(...)
from json import dumps           # 只取一件,直接用 dumps(...)
from json import dumps as jd     # 取一件再改名(防冲突)

慎用 from xxx import *:把整个空间倒进当前空间——名字冲突、来源不明,readability 杀手(6-5 的老戒律)。

三、软件包:一个目录

模块多了再升级:包(package)=含 __init__.py 的目录(Python 3.3+ 裸目录也行,但有 __init__.py 意图最清楚),子模块按目录层级组织:

myshop/                  ← 包
├── __init__.py
├── auth/                ← 子包
│   ├── __init__.py
│   └── user.py
└── pay/
    └── alipay.py

用法:from myshop.auth.user import User。包解决的不是重名是组织——【支付相关都在 pay、认证相关都在 auth】,新人看目录树就懂代码地图。第三方库(pip install 的那些)全是包——pip 装的其实就是别人写好的包。

四、动手 Demo:三层分区实战

# —— 命名空间演示:两个【同名类】和平共处 ——
import types

auth = types.SimpleNamespace()          # 模拟一个 auth 空间
shop = types.SimpleNamespace()          # 模拟一个 shop 空间

class User:
    def __init__(self, name, pwd):
        self.name, self.pwd = name, pwd
    def __repr__(self):
        return f"认证用户({self.name})"

auth.User = User                        # auth.User

class User:                             # 第二次定义同名类:直接覆盖!
    def __init__(self, name, cart):
        self.name, self.cart = name, cart
    def __repr__(self):
        return f"购物用户({self.name}, {self.cart}件)"

shop.User = User                        # shop.User

u1 = auth.User("波仔", "pw")            # 引用早先保存的类对象
u2 = shop.User("波仔", 3)
print(u1, "/", u2)
print("同名不同物:", auth.User is not shop.User)

# —— 模块查找:sys.path 决定 import 能找到谁 ——
import sys
print("模块搜索路径条数:", len(sys.path))
print("json 来自:", __import__("json").__file__.split("\\")[-1])

沙箱实跑输出:

认证用户(波仔) / 购物用户(波仔, 3件)
同名不同物: True
模块搜索路径条数: 7
json 来自: __init__.py

看到什么算成功:两个 User 各自正常实例化、is not 证明是两个类;import 系统正常工作(路径条数因机器环境而异,不必对数)。

读结果:同一文件里连续定义两个 User,第二个直接覆盖第一个——同空间内同名必冲突;但各自挂到 auth./shop. 空间后就和平共处。模块化不是锦上添花,是百类以上项目的生存条件。

五、30 秒实验

打印 type(sys).__module__ 或任意类的 __module__ 属性——看到它属于哪个模块。每个对象都自带出生地标签,排查【这个类哪来的】一查便知。

六、常见误区

误区 真相
import * 省事 名字冲突来源不明;明确 import 才可读可搜
包只是文件夹 __init__.py 控制包的初始化与导出面;裸目录行为易踩坑
全部塞一个文件也行 超过几百行的单文件已经难导航;按职责拆模块是基本卫生
自测三问(点击展开) 1. **模块和包的关系?** 答:模块=一个 .py 文件;包=模块的目录(带 __init__.py),层级化组织。 2. **Python 防重名的机制靠什么?** 答:每个模块天然是独立命名空间,跨空间用 模块名.名字 访问。 3. **为什么慎用 from xxx import *?** 答:把外部名字全倒进来,冲突不可见、来源不可查。

相关笔记