你是否也有这样的习惯:编写 Python 代码时,第一反应是“我自己写一个工具函数”,而不是先想想“标准库里是不是已经有现成的了”?
我曾经也是如此,直到有一天回顾项目代码,发现自己写了三个版本的“递归遍历文件夹”,两个“重试装饰器”,还有一堆手动拼接 URL、计算时间差的零散函数,看着都让人头疼。
因此,今天我们就秉持“别再重复造轮子”的理念,深入聊聊 Python 标准库里 10 个非常实用的内置模块。它们无需任何额外安装,直接 import 即可使用。以后当你再想实现某个功能时,不妨先问自己一句:Python 自己是不是已经提供了?
1) os:与操作系统交互的“门面”
日常开发中,诸如创建文件夹、删除文件、读取环境变量、切换工作目录等操作,很多人的第一反应是自己拼接路径、手动判断。
其实,绝大部分需求用几行 os 模块的代码就能优雅解决。
import os
# 获取环境变量,并提供默认值
db_url = os.getenv("DB_URL", "sqlite:///default.db")
# 确保目录存在,避免重复创建报错
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)
# 递归遍历目录下的所有文件
for root, dirs, files in os.walk("."):
for name in files:
if name.endswith(".py"):
print(os.path.join(root, name))
像 os.makedirs(..., exist_ok=True) 这样的细节就非常贴心:即使目录已存在也不会抛出异常,省去了很多手动的 if 判断。
2) pathlib:告别手动拼接路径字符串
虽然 os.path 下的函数集合也能完成路径操作,但用多了难免显得杂乱。pathlib 模块将路径抽象为对象,操作起来更加直观和方便。
from pathlib import Path
base = Path(__file__).parent
data_dir = base / "data"
data_dir.mkdir(exist_ok=True)
log_file = data_dir / "app.log"
# 判断是否存在、读取、写入文件
if log_file.exists():
print(log_file.read_text(encoding="utf-8"))
log_file.write_text("hello, pathlib\n", encoding="utf-8")
你看,(base / "data" / "app.log") 这种写法,比使用 "{}{}{}".format(...) 或 os.path.join(...) 清晰易读得多,并且它能自动处理不同操作系统的路径分隔符问题。
3) collections:别再手动实现“计数器”和“默认字典”
许多人进行数据统计时,仍然在用以下方式:
d = {}
for x in data:
if x not in d:
d[x] = 0
d[x] += 1
标准库直接提供了一个专为此场景设计的 Counter。
from collections import Counter, defaultdict
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
counter = Counter(words)
print(counter.most_common(2)) # [('apple', 3), ('banana', 2)]
# 默认字典:自动为不存在的键创建初始值
index = defaultdict(list)
for i, word in enumerate(words):
index[word].append(i)
print(index["apple"]) # [0, 2, 5]
defaultdict(list) 这种模式在进行数据分组、构建索引或聚合时特别方便。很多人还在用 if key not in d: d[key] = [] 这样的写法,其实完全没必要。
只要你开始编写“嵌套循环”、“排列组合”、“滑动窗口”这类逻辑,都应该先想想 itertools 模块是否已经提供了现成的“轮子”。合理利用这些工具能让你在 开源实战 中更专注于业务逻辑,而非底层实现。
import itertools as it
nums = [1, 2, 3, 4]
# 所有两两组合(不考虑顺序)
for a, b in it.combinations(nums, 2):
print(a, b)
# 笛卡尔积
colors = ["red", "blue"]
sizes = ["S", "M", "L"]
for c, s in it.product(colors, sizes):
print(c, s)
# 累积求和
for s in it.accumulate(nums):
print(s)
许多代码中那些三四层的 for 循环,其实用 product 一行就能清晰描述,逻辑也更不容易出错。
这个模块不那么起眼,但它打包了几个常用的高级工具,例如用于缓存函数结果的装饰器、预先绑定部分参数的偏函数,以及用于生成排序键的函数。
最常见的就是 lru_cache。如果你编写递归函数或频繁查询配置的函数,只需添加一层缓存装饰器,性能立刻就能得到显著提升。
from functools import lru_cache, partial
@lru_cache(maxsize=128)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(40)) # 有了缓存,计算速度飞快
# 偏函数:预先为函数填充部分参数
int2 = partial(int, base=2)
print(int2("1010")) # 10
许多人为了“缓存结果”自己维护一个全局字典,还要考虑线程安全等问题。其实在多数场景下,lru_cache 装饰器就足够使用了。
6) datetime:别再用字符串和手动计算时间戳了
时间处理是容易出错的领域,手动拼接字符串或进行时间戳运算极易踩坑,尤其是在处理时区的时候。
from datetime import datetime, timedelta, timezone
# 当前时间
now = datetime.now()
# 转换为字符串
s = now.strftime("%Y-%m-%d %H:%M:%S")
print("现在:", s)
# 字符串解析为时间对象
dt = datetime.strptime("2025-01-01 12:30:00", "%Y-%m-%d %H:%M:%S")
# 计算时间差
delta = now - dt
print("相差天数:", delta.days)
# 处理时区
tz = timezone(timedelta(hours=8)) # 东八区
now_cn = datetime.now(tz=tz)
print("东八区时间:", now_cn.isoformat())
一旦场景变得复杂(跨天、跨时区、计算间隔),就不要再使用“手动计算秒数”的方式了。使用 datetime 模块能让时间处理变得清晰明了。
7) json:处理配置文件或接口数据时的首选
以前人们喜欢用 split("=") 来解析配置文件,或者手动拼接 JSON 字符串。现在基本上都会直接使用 JSON 格式,一是标准规范,二是便于与前端或其他编程语言交互。
import json
from pathlib import Path
config_path = Path("config.json")
# 写入配置
config = {
"host": "127.0.0.1",
"port": 8080,
"debug": True,
"features": ["cache", "metrics"]
}
config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False), encoding="utf-8")
# 读取配置
raw = config_path.read_text(encoding="utf-8")
loaded = json.loads(raw)
print(loaded["host"], loaded["port"])
善用 indent(缩进)和 ensure_ascii(确保非ASCII字符正确显示)这些参数,能让你的配置文件瞬间变得清爽易读。
8) logging:是时候告别满屏的 print 语句了
在项目初期,随意使用 print 调试或许没问题。但随着项目规模扩大,排查问题时需要靠肉眼在日志中搜索 print 输出,那就非常痛苦了。标准库自带的 logging 模块,功能足够强大,能满足很长一段时间的需求。
import logging
from pathlib import Path
log_file = Path("app.log")
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
handlers=[
logging.FileHandler(log_file, encoding="utf-8"),
logging.StreamHandler()
]
)
logger = logging.getLogger("demo")
logger.info("服务启动了")
logger.warning("这个配置有点奇怪:%s", {"debug": True})
logger.error("出错了", exc_info=True)
具备日志级别、时间戳、输出到文件等功能后,后续接入日志分析平台或过滤关键信息都会非常方便,远比在代码中到处插入 print("here") 要可靠得多。
9) argparse:让命令行脚本瞬间专业起来
许多人编写脚本时,将参数全部硬编码在文件里,想改个路径还得打开源代码修改。更规范的做法是使用 argparse 来处理命令行参数,这能让你的脚本立刻变成一个真正的“命令行工具”。
import argparse
from pathlib import Path
def main():
parser = argparse.ArgumentParser(description="简单的文件统计工具")
parser.add_argument("path", help="要统计的目录")
parser.add_argument("--ext", default=".py", help="只统计某种后缀的文件")
args = parser.parse_args()
base = Path(args.path)
count = 0
for p in base.rglob(f"*{args.ext}"):
if p.is_file():
count += 1
print(f"{base} 下共有 {count} 个 {args.ext} 文件")
if __name__ == "__main__":
main()
以后使用这个脚本时,命令就变成了:
python count_files.py . --ext .txt
一旦你开始这样编写脚本,并交给他人使用几次,你就会自然而然地延续这种模式。
10) concurrent.futures:简单的并发任务,无需从零构建
如果你自己去操作 threading.Thread 或 multiprocessing.Process,再加上队列、锁等机制,代码会很快变得复杂。对于许多“简单的并发任务”,例如批量网络请求或批量 IO 操作,使用 concurrent.futures 提供的线程池或进程池就足够了。这正是在 后端 & 架构 场景中处理并发问题的优雅方式之一。
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
import random
def fetch(url: str) -> tuple[str, float]:
# 模拟网络请求
cost = random.uniform(0.1, 0.5)
time.sleep(cost)
return url, cost
urls = [f"https://example.com/{i}" for i in range(10)]
with ThreadPoolExecutor(max_workers=5) as executor:
future_map = {executor.submit(fetch, url): url for url in urls}
for future in as_completed(future_map):
url, cost = future.result()
print(f"{url} 用时 {cost:.3f}s")
许多诸如“写个简单爬虫”或“并发调用几个 API”的场景,使用这个模块比自己手动管理线程要省心得多。
总结
实际上,Python 标准库中能派上用场的模块远不止这 10 个,像 re(正则表达式)、subprocess(调用外部命令)、typing / dataclasses(结构化数据)、sqlite3(轻量级数据库)等也都非常实用。
但如果你能先将上述这 10 个模块运用熟练,你会逐渐发现:很多“小轮子”根本不需要你亲手打造,Python 官方已经为你精心打磨好了一套工具箱。你需要做的,就是养成在编码前先去工具箱里翻找的习惯。
下次编写脚本或功能时,不妨先停顿片刻,问自己一句:“这个功能,Python 标准库里是不是已经有现成的模块了?” 养成这个习惯,将极大提升你的开发效率与代码质量。如果你想深入探讨更多 Python 技巧或分享你的使用心得,欢迎来 云栈社区 与广大开发者交流。