模块与包:import 用法与标准库宝藏库巡礼
文章目录
前提知识:类与对象:Python 的面向对象初体验,像搭积木一样写代码
适合读者:掌握了类与对象基础、想了解 Python 代码组织和标准库的开发者
想象一家餐厅的后厨。
厨房里有很多厨师,每个厨师各有专长:有的专做前菜,有的负责主菜,有的专精甜点。客人不需要知道厨师叫什么名字,只需要看菜单点菜,后厨自然会安排对应的厨师出品。
在 Python 的世界里,模块(module)就是一位厨师,包(package)就是整个厨房。import 关键字是点菜单——告诉 Python:“我需要某个厨师的那道菜。”
本篇用一个餐厅每日特价菜生成器作为贯穿场景:基于菜单数据,随机选出一道今日特价菜,并标注推荐理由和时间戳。这个小工具会把 random、datetime、os 三个标准库模块串在一起,让读者感受到标准库的实用价值。
一、模块、包与 import 的本质
1.1 三个概念一次理清
| 概念 | 餐厅类比 | 实际例子 | 本质 |
|---|---|---|---|
| 模块 | 一位厨师 | random.py、math.py |
一个 .py 文件 |
| 包 | 整个厨房 | os、collections |
一个文件夹,内含 __init__.py |
| 内置函数 | 厨房自带的万能工具 | print()、len()、type() |
Python 解释器直接提供,无需导入 |
# 内置函数,直接可用
print("Hello") # 不需要 import
# math 是 Python 自带的标准库模块
import math
math.sqrt(16) # → 4.0
math 这个文件在哪里?它是 Python 解释器安装时自带的,不需要额外安装。如果想知道模块的具体路径:
import math
print(math.__file__)
# Linux/macOS: /usr/lib/python3.12/lib-dynload/math.cpython-312-x86_64-linux-gnu.so
# Windows: C:\Users\xxx\AppData\Local\Programs\Python\Python312\lib\math.py
1.2 import 的三种写法
# 写法一:导入整个模块(推荐,明确知道函数来自哪里)
import math
print(math.sqrt(16)) # → 4.0
# 写法二:只导入需要的函数或变量(使用更简洁,但可能有命名冲突)
from math import sqrt, pi
print(sqrt(16) * pi) # → 12.566...
# 写法三:给模块或函数起别名(避免名字太长,或绕开命名冲突)
import numpy as np
from datetime import datetime as dt
np.array([1, 2, 3])
dt.now()
三种写法的权衡:
经验之谈:代码库中
import xxx的写法是最稳妥的——即使名字长一点,但任何人读代码时都能一眼看出sqrt来自math模块,而不是自己定义的函数或第三方库。
1.3 __name__ == "__main__" 的原理
这是 Python 中最容易让人困惑又最重要的知识点之一。先看代码:
文件:greet.py
def say_hello():
print("Hello!")
# 直接运行此文件时执行这里
if __name__ == "__main__":
print("正在作为主程序运行")
say_hello()
$ python greet.py
正在作为主程序运行
Hello!
如果这个文件被其他文件导入,if __name__ == "__main__": 下的代码不会执行:
文件:main.py
import greet # 导入 greet 模块
print("main.py 正在运行")
greet.say_hello()
$ python main.py
main.py 正在运行
Hello!
原因在于 __name__ 变量的值:
| 场景 | __name__ 的值 |
if __name__ == "__main__" |
|---|---|---|
直接运行 python xxx.py |
"__main__" |
执行 |
被其他文件 import |
"模块名"(如 "greet") |
跳过 |
这个机制有什么用?让同一个 .py 文件既能作为主程序运行,又能作为模块被其他文件导入。几乎所有 Python 项目的入口文件都有这一行:
# 几乎所有项目的入口文件结构
def main():
# 程序的主要逻辑写在这里
...
if __name__ == "__main__":
main()
二、标准库宝藏库:5 个最常用的模块
Python 自带的标准库极其丰富——不需要 pip install,不需要联网,开箱即用。以下 5 个模块覆盖了日常开发的绝大多数场景。
2.1 math —— 数学运算
import math
# 常用函数
math.sqrt(16) # → 4.0,平方根
math.pow(2, 10) # → 1024.0,幂运算(返回 float)
math.floor(3.7) # → 3,向下取整
math.ceil(3.2) # → 4,向上取整
math.fabs(-5) # → 5.0,绝对值(返回 float)
# 常量
math.pi # → 3.141592653589793
math.e # → 2.718281828459045
# 三角函数
math.sin(math.pi / 2) # → 1.0
math.cos(0) # → 1.0
math.radians(180) # → 3.14159...,角度转弧度
注意:
math.pow(2, 10)返回float(1024.0),如果需要整数结果,用2 ** 10。
2.2 random —— 随机数生成
这是日常 Python 开发中使用频率非常高的模块——随机选菜、洗牌、打乱顺序这些场景都靠它。
import random
# 基本随机
random.randint(1, 6) # → 随机整数 [1, 6](两端都包含)
random.random() # → [0.0, 1.0) 之间的随机浮点数
random.uniform(1.5, 9.3) # → [1.5, 9.3] 之间的随机浮点数
# 从序列中随机选择
menu = ["红烧肉", "清蒸鱼", "宫保鸡丁", "麻婆豆腐"]
random.choice(menu) # → 随机返回一道菜
random.sample(menu, 2) # → 随机返回 2 道菜(不重复)
# 打乱顺序(洗牌)
cards = ["A", "2", "3", "4", "5"]
random.shuffle(cards) # cards 顺序被随机打乱
print(cards) # → 类似 ['3', 'A', '5', '2', '4']
2.3 datetime —— 日期与时间
import datetime
# 获取当前时间
now = datetime.datetime.now()
print(now)
# → 2026-04-27 22:30:15.123456
# 格式化输出(最常用)
print(now.strftime("%Y-%m-%d %H:%M"))
# → 2026-04-27 22:30
print(now.strftime("%A, %B %d")) # 英文全称
# → Monday, April 27
# 时间差计算
import datetime
tomorrow = now + datetime.timedelta(days=1)
print(tomorrow.strftime("%Y-%m-%d"))
# → 2026-04-28
one_hour_ago = now - datetime.timedelta(hours=1)
print(one_hour_ago.strftime("%H:%M"))
# → 21:30
# 字符串转时间对象
time_str = "2026-04-27 18:00:00"
dt = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
print(dt) # → 2026-04-27 18:00:00
| 占位符 | 含义 | 示例 |
|---|---|---|
%Y |
四位年份 | 2026 |
%m |
两位月份 | 04 |
%d |
两位日期 | 27 |
%H |
24小时制小时 | 22 |
%M |
分钟 | 30 |
%S |
秒 | 15 |
%A |
星期几(英文) | Monday |
2.4 os —— 操作系统接口
import os
# 文件路径(跨平台拼接,避免手动加斜杠)
path = os.path.join("data", "menu", "dishes.txt")
print(path)
# Linux/macOS: data/menu/dishes.txt
# Windows: data\menu\dishes.txt
# 检查文件/目录是否存在
os.path.exists("menu.py") # → True / False
os.path.isfile("menu.py") # → True / False(是文件吗)
os.path.isdir("data") # → True / False(是目录吗)
# 获取当前工作目录
print(os.getcwd())
# → /home/user/project 或 C:\Users\xxx\project
# 创建目录
os.makedirs("output", exist_ok=True) # exist_ok=True 表示已存在也不报错
# 列目录文件
files = os.listdir(".")
print([f for f in files if f.endswith(".py")])
# 环境变量
home = os.environ.get("HOME") or os.environ.get("USERPROFILE")
print(f"用户主目录: {home}")
2.5 sys —— 系统与解释器
import sys
# 退出程序(慎用,强制终止整个进程)
# sys.exit(0) # 0 表示正常退出,非 0 表示异常退出
# 命令行参数(从 argv[1] 开始是传入的参数)
# python main.py arg1 arg2
# sys.argv = ["main.py", "arg1", "arg2"]
print(sys.argv)
# Python 解释器路径
print(sys.executable)
# → /usr/bin/python3 或 C:\Users\xxx\AppData\Local\Programs\Python\Python312\python.exe
# 查看已加载的模块
print(sys.modules.keys())
三、用 dir() 和 help() 自主探索模块
大多数教程只讲已经被讲烂的那几个函数——但 Python 标准库有 200+ 个模块,没人能全部记住。真正重要的能力是自己发现模块里有什么,而不是依赖别人告诉你。
3.1 dir() —— 列出模块的所有属性
import math
# 不带参数:列出当前作用域的所有名字
print(dir())
# → ['__annotations__', '__builtins__', '__doc__', 'math', ...]
# 带参数:列出对象的所有属性和方法(双下划线开头的是 Python 内部使用的)
attrs = dir(math)
print(attrs)
# → ['__doc__', '__file__', '__loader__', '__name__', '__package__',
# 'acos', 'acosh', 'asin', ... 'sqrt', 'tan', 'tanh', 'trunc']
过滤出以字母开头的"实用"成员(去掉双下划线开头的内部属性):
import math
practical = [a for a in dir(math) if not a.startswith("_")]
print(practical)
# → ['acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', ... 'sqrt', 'tan', 'tanh', 'trunc']
3.2 help() —— 查看函数的文档
import random
# 查看某个函数的用法
help(random.choice)
输出:
Help on method choice in module random:
choice(self, seq) method of random.Random instance
Choose a random element from a non-empty sequence.
简洁用法:只读 docstring 字符串本身:
print(random.choice.__doc__)
# → Choose a random element from a non-empty sequence.
经验之谈:在实际写代码时,如果知道大概要做什么但不确定用哪个函数,先
import xxx,然后print(dir(xxx))扫一遍成员名字,往往能找到惊喜——比如os.path.splitext()这个函数,名字本身就说明了它的用途。
四、综合实战:餐厅每日特价生成器
4.1 需求分析
一个餐厅老板希望每天早上自动生成一道"今日特价菜",包含:
- 从固定菜单中随机选一道菜
- 给出推荐理由
- 标注生成时间
- 如果当日菜品已经生成过(文件存在),直接读取而不是重复生成
4.2 完整代码
# daily_special.py
import os
import random
import datetime
import json
MENU_FILE = "menu.json"
SPECIAL_FILE = "today_special.json"
def load_menu():
"""从 JSON 文件加载菜单数据"""
if not os.path.exists(MENU_FILE):
# 如果菜单文件不存在,创建默认菜单
default_menu = {
"appetizers": ["凉拌黄瓜", "老醋花生", "拍黄瓜"],
"main_courses": ["红烧肉", "清蒸鲈鱼", "宫保鸡丁", "麻婆豆腐", "鱼香肉丝"],
"desserts": ["红糖糍粑", "冰粉", "芝麻汤圆"]
}
with open(MENU_FILE, "w", encoding="utf-8") as f:
json.dump(default_menu, f, ensure_ascii=False, indent=2)
return default_menu
with open(MENU_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def generate_reason(dish, category):
"""根据菜品和类别生成推荐理由"""
reasons = {
"appetizers": [
f"{dish} 开胃解腻,是整顿饭的好开头",
f"{dish} 清爽可口,唤醒味蕾",
],
"main_courses": [
f"{dish} 精选食材,当日主推",
f"{dish} 大厨拿手菜,不容错过",
f"{dish} 香气扑鼻,回头客必点",
],
"desserts": [
f"{dish} 甜蜜收尾,一整天的好心情",
f"{dish} 手工制作,限量供应",
],
}
return random.choice(reasons.get(category, [f"{dish} 今日特价"]))
def pick_special(menu):
"""从菜单中随机选取一道今日特价菜"""
category = random.choice(list(menu.keys()))
dish = random.choice(menu[category])
reason = generate_reason(dish, category)
return {
"dish": dish,
"category": category,
"reason": reason,
"generated_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
}
def save_special(special):
"""将今日特价保存到文件"""
with open(SPECIAL_FILE, "w", encoding="utf-8") as f:
json.dump(special, f, ensure_ascii=False, indent=2)
def load_special():
"""从文件读取今日特价(如果已存在)"""
if os.path.exists(SPECIAL_FILE):
with open(SPECIAL_FILE, "r", encoding="utf-8") as f:
return json.load(f)
return None
def main():
# 先检查今日特价是否已生成过
today_special = load_special()
today_date = datetime.datetime.now().strftime("%Y-%m-%d")
if today_special and today_special.get("generated_at", "").startswith(today_date):
print(f"今日特价已生成({today_special['generated_at']}),直接读取:")
else:
menu = load_menu()
today_special = pick_special(menu)
save_special(today_special)
print(f"今日特价已生成({today_special['generated_at']}):")
# 输出格式化的今日特价
category_names = {
"appetizers": "前菜",
"main_courses": "主菜",
"desserts": "甜点",
}
cat_name = category_names.get(today_special["category"], today_special["category"])
print("=" * 30)
print(f" 🍽️ 今日特价 · {cat_name}")
print(f" 菜名:{today_special['dish']}")
print(f" 推荐理由:{today_special['reason']}")
print(f" 生成时间:{today_special['generated_at']}")
print("=" * 30)
if __name__ == "__main__":
main()
4.3 运行效果
第一次运行(生成特价菜):
今日特价已生成(2026-04-27 08:05):
==============================================================
🍽️ 今日特价 · 主菜
菜名:宫保鸡丁
推荐理由:宫保鸡丁 大厨拿手菜,不容错过
生成时间:2026-04-27 08:05
==============================================================
当日第二次运行(读取缓存):
今日特价已生成(2026-04-27 08:05),直接读取:
==============================================================
🍽️ 今日特价 · 主菜
菜名:宫保鸡丁
推荐理由:宫保鸡丁 大厨拿手菜,不容错过
生成时间:2026-04-27 08:05
==============================================================
4.4 流程图
五、常见易错点
5.1 导入顺序错误
Python 会根据 sys.path 中的路径顺序查找模块——当前脚本所在目录在最前面,然后是 Python 标准库,最后是第三方库。
# 错误示例:当前目录下有一个名为 random.py 的文件
# 会覆盖标准库的 random!
import random
print(random.randint(1, 6)) # 报错:AttributeError(如果自定义的 random.py 没有 randint)
解决方法:永远不要用标准库的名字(random、os、sys、math 等)命名自己的文件。
5.2 模块只导入一次
同一个模块被多次 import,Python 只会在第一次执行初始化,后续 import 直接返回已加载的对象。
import math
import math # 这次 import 什么都不做,math 模块对象不变
print(math is math) # → True,同一个对象
如果修改了模块文件,需要重启 Python 解释器才能生效——Python 不支持热重载自己。
5.3 相对导入的限制
包内部可以使用相对导入(如 from . import utils),但不能用相对导入运行独立文件:
restaurant/
├── __init__.py
├── daily_special.py # 如果直接运行 python daily_special.py
└── menu_utils.py # 这里的 from . import menu_utils 会报错!
解决方法:始终用 python -m 包名.模块名 或 python 入口文件.py 的方式运行,确保 Python 路径正确。
六、知识结构图
七、工程原则
-
标准库优先:遇到常见需求(随机数、日期时间、文件操作、JSON 序列化)先查标准库,不需要 pip install 就能用,稳定性和兼容性都有保障。
-
import xxx的命名空间写法优先于from xxx import yyy:显式写出模块名虽然多敲几个字,但代码可读性大幅提升——读者始终知道sqrt来自math而不是自己定义的函数。 -
用
dir()和help()做自主探索:没有人能记住标准库的全部内容,遇到问题时的第一反应应该是"这个模块里有没有现成的函数",而不是"去 pip 一个第三方库"。 -
不要用标准库名字命名自己的文件:
random.py、test.py、string.py这些文件名会悄悄覆盖 Python 内置模块,产生难以排查的 Bug。 -
__name__ == "__main__"是 Python 项目的标准入口约定:所有需要直接运行的文件都应该有这个结构,所有只需要被导入的代码都不应该出现在这个分支下。
如果觉得这篇文章有帮助,欢迎点赞、评论和关注。往期的函数与异常处理、类与对象等文章中也有类似的实战项目,可以结合阅读。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)