11 - 模块与包

11 - 模块与包

写到一定程度的代码,你会发现把所有东西塞在一个文件里太乱了。模块和包就是帮你组织代码的。


模块是什么

简单说,一个 .py 文件就是一个模块。你之前写的 hello.pycalculator.py 都是模块。

模块的好处是可以互相引用。比如你写了一个工具函数放在 utils.py 里,其他文件可以直接拿来用。


创建和使用模块

先创建一个 utils.py

python 复制代码
# utils.py

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

PI = 3.14159

然后在另一个文件里使用它:

python 复制代码
# main.py

import utils

print(utils.add(3, 5))    # 8
print(utils.PI)           # 3.14159

import utils 就是把 utils.py 这个模块导进来,然后用 模块名.函数名 的方式调用。


import 的几种写法

python 复制代码
# 方式一:导入整个模块
import utils
utils.add(3, 5)

# 方式二:导入特定的东西
from utils import add, PI
add(3, 5)  # 直接用,不用加模块名

# 方式三:导入全部(不推荐)
from utils import *
add(3, 5)

# 方式四:起别名
import utils as u
u.add(3, 5)

from utils import add as plus
plus(3, 5)

方式三为什么不好?因为你不知道导进来了什么名字,可能跟你自己定义的名字冲突。在大型项目里这种 bug 很难查。

不过有一个例外,在 __init__.py 里用 from xxx import * 是常见的做法,后面讲包的时候会说到。


__name__ 变量

这个在很多 Python 文件底部都能看到:

python 复制代码
def main():
    print("这是主逻辑")

if __name__ == "__main__":
    main()

这是什么意思呢?

每个 Python 文件有一个内置变量 __name__

  • 如果这个文件是直接运行 的,__name__ 的值是 "__main__"
  • 如果这个文件是被 import 的,__name__ 的值是模块名

举个例子。假设 utils.py 里有:

python 复制代码
# utils.py
def add(a, b):
    return a + b

print("utils 被加载了")

if __name__ == "__main__":
    print("utils 是直接运行的")
    print(add(1, 2))

直接运行 uv run utils.py

复制代码
utils 被加载了
utils 是直接运行的
3

在别的文件里 import utils

复制代码
utils 被加载了

"utils 是直接运行的"那段代码就不会执行。

所以 if __name__ == "__main__" 的意思就是"如果这个文件是被直接运行的(而不是被导入的),就执行下面的代码"。这样你的模块既可以被导入使用,也可以直接运行做测试。


标准库模块

Python 自带了很多实用的模块,不需要安装,直接 import 就能用。

python 复制代码
# os --- 操作系统相关
import os
print(os.getcwd())        # 当前目录
print(os.listdir("."))    # 列出目录下的文件

# sys --- 系统相关
import sys
print(sys.version)        # Python 版本
print(sys.argv)           # 命令行参数

# math --- 数学函数
import math
print(math.sqrt(16))      # 4.0
print(math.ceil(3.2))     # 4(向上取整)
print(math.floor(3.8))    # 3(向下取整)

# random --- 随机数
import random
print(random.randint(1, 100))       # 1-100 之间的随机整数
print(random.choice(["a", "b", "c"])) # 随机选一个
random.shuffle(my_list)              # 打乱列表

# datetime --- 日期时间
from datetime import datetime, timedelta
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))
tomorrow = now + timedelta(days=1)

# json --- JSON 处理
import json
data = {"name": "小明", "age": 25}
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)
parsed = json.loads(json_str)

# re --- 正则表达式(第 19 章详细讲)
import re
result = re.findall(r"\d+", "我有 3 个苹果和 5 个橘子")
print(result)  # ['3', '5']

这些标准库是 Python 的"电池"------Python 号称"自带电池"的语言,就是因为它附带了这些好用的模块。


第三方包

标准库不够用的时候,就需要安装第三方包了。还记得第一章说的 uv 吗?就是干这个的。

安装包

bash 复制代码
# 安装 requests(HTTP 请求库)
uv add requests

# 安装指定版本
uv add "requests>=2.28,<3.0"

# 安装多个
uv add requests beautifulsoup4 pandas

使用包

python 复制代码
import requests

# 发送一个 GET 请求
response = requests.get("https://httpbin.org/get")
print(response.status_code)  # 200
print(response.json())       # 解析 JSON 响应

卸载包

bash 复制代码
uv remove requests

查看已安装的包

bash 复制代码
uv pip list

pyproject.toml

uv init 创建项目时会自动生成 pyproject.toml,它记录了项目的依赖:

toml 复制代码
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"

[tool.uv]
dev-dependencies = []

你用 uv add 安装的包会自动添加到这个文件里。别人拿到你的项目后,只需要 uv sync 就能安装所有依赖。


包(Package)

包就是"一堆模块的集合",本质上是一个包含 __init__.py 文件的目录。

复制代码
my_package/
├── __init__.py
├── module_a.py
├── module_b.py
└── sub_package/
    ├── __init__.py
    └── module_c.py

使用方式:

python 复制代码
from my_package import module_a
from my_package.sub_package import module_c

__init__.py 可以是空文件,它的存在只是告诉 Python "这个目录是个包"。你也可以在里面写代码,通常用来控制包的导入行为:

python 复制代码
# my_package/__init__.py
from .module_a import func_a
from .module_b import func_b

# 这样用户就可以直接写:
# from my_package import func_a
# 而不用写:
# from my_package.module_a import func_a

虚拟环境(再讲一次)

第一章简单提了一下,这里再深入一点。

为什么需要虚拟环境?假设你有两个项目:

  • 项目 A 需要 requests 2.28
  • 项目 B 需要 requests 2.31

如果都装在系统全局,就冲突了。虚拟环境让每个项目有自己独立的依赖空间。

用 uv 的话,你在项目目录里执行 uv init 的时候它就自动创建了。所有通过 uv add 安装的包都只在这个项目里有效,不影响其他项目。

bash 复制代码
# 项目 A
cd project_a
uv add "requests==2.28"

# 项目 B
cd project_b
uv add "requests==2.31"

# 互不影响

一个实际项目的结构

当你写一个稍微大点的项目时,结构大概是这样的:

复制代码
my_project/
├── pyproject.toml
├── README.md
├── src/
│   └── my_project/
│       ├── __init__.py
│       ├── main.py
│       ├── utils.py
│       └── config.py
├── tests/
│   ├── __init__.py
│   └── test_main.py
└── scripts/
    └── setup.sh

不需要一开始就这么复杂。小项目一个 .py 文件就够了,等项目变大了再拆分。


本章小结

  • 一个 .py 文件就是一个模块,用 import 导入
  • from xxx import yyy 可以导入特定的函数或变量
  • if __name__ == "__main__" 判断文件是被直接运行还是被导入的
  • Python 标准库自带很多实用模块(os、sys、json、datetime 等)
  • uv add 安装第三方包,uv remove 卸载
  • 包是包含 __init__.py 的目录,用来组织多个模块
  • 虚拟环境让不同项目的依赖互不影响

面试题

Q1:import modulefrom module import func 有什么区别?
点击查看答案

  • import module:导入整个模块,使用时需要加前缀 module.func()
  • from module import func:只导入特定对象,直接调用 func()

区别:

  1. 命名空间import module 不会污染当前命名空间,from module import func 直接把 func 放进来了
  2. 可读性module.func() 一眼就知道 func 来自哪个模块
  3. 冲突风险from module import * 最容易冲突,不推荐

推荐:大多数情况用 import modulefrom module import specific_name

Q2:if __name__ == "__main__" 的作用是什么?
点击查看答案

判断当前模块是被直接运行还是被导入的。

  • 直接运行脚本时,__name__ 等于 "__main__"
  • 被其他文件 import 时,__name__ 等于模块名

典型用途:

  1. 在模块底部放测试代码,导入时不会执行
  2. 提供命令行入口,python script.py 时执行主逻辑
  3. 模块既可以当库用,也可以当脚本用

Q3:Python 的模块搜索路径是怎样的?
点击查看答案

import xxx 时,Python 按以下顺序搜索:

  1. 内置模块(如 sys、os)
  2. sys.path 中的目录 ,包括:
    • 当前脚本所在目录
    • PYTHONPATH 环境变量中的目录
    • Python 安装目录下的标准库
    • 第三方包安装目录(site-packages)

可以通过 sys.path 查看完整搜索路径。不建议手动修改 sys.path,用虚拟环境管理依赖更规范。

Q4:什么是循环导入?怎么解决?
点击查看答案

循环导入是指模块 A 导入模块 B,模块 B 又导入模块 A,形成循环依赖。

python 复制代码
# a.py
from b import func_b
# b.py
from a import func_a

这会导致 ImportError 或部分导入(某些名字找不到)。

解决方法:

  1. 重构代码:把公共部分提取到第三个模块,打破循环
  2. 延迟导入:把 import 语句放到函数内部
  3. 导入模块而非名字 :用 import b 代替 from b import func_b

根本原因是模块设计耦合度太高,最好的方式是重构。


相关推荐
念恒123061 小时前
MySQL索引
数据库·mysql
Lao A(zhou liang)的菜园1 小时前
如何快速诊断Oracle性能问题?
数据库·oracle
铁皮哥1 小时前
【agent 开发】Claude Code 的 Skill 是怎么被加载的?从 name/description 到 SKILL.md 再到资源文件
java·服务器·数据库·python·gitee·github·软件工程
小小小小宇1 小时前
前端 Redux applyMiddleware 中间件链原理
前端
py小王子1 小时前
期刊复现|Python 实现带误差棒与3D 柱状数据可视化
python·期刊复现
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年5月25日
大数据·人工智能·python·信息可视化·自然语言处理·ai编程
是你就无限6151 小时前
FastAPI 核心技术与实战
python·fastapi
是上好佳佳佳呀1 小时前
【Python基础|DAY05】Python 模块与包
python
一只fish2 小时前
Oracle官方文档翻译《Database Concepts 26ai》第14章-物理存储结构
数据库·oracle