代码多了必须分区管理:命名空间防重名(同名各归各家);模块=一个文件(Python 的 .py);软件包=一个目录(模块的文件夹)。三层由小到大:名字不冲突 → 文件可复用 → 目录成体系。
一、命名空间:同名不打架
几百个类里【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 *?** 答:把外部名字全倒进来,冲突不可见、来源不可查。- ☐ 5-22 学习完成
相关笔记
- C++ 10.6 · 命名空间 —— namespace 关键字的 C++ 画法
- 树莓派 6-5 · 函数与模块 —— import 三写法的出处
- ⌨️ 编程原理 · 索引 —— 全课路线图