10. 标准库速览(一)
对应官方文档:10. Brief Tour of the Standard Library 一句话导读:这一章解决「装完 Python 到底白送了些什么」——把最常用的十几组模块挨个摸一遍,学完知道遇事别急着
pip install,标准库里八成已经有轮子了。
前面几章我们把语法啃完了,从这一章起官方换了个节奏:不再教新语法,而是带你逛「随 Python 一起装好的工具箱」。村长按 10.1 到 10.12 的编号走,每节两三句人话加一段能直接跑的短代码。所有例子都不碰你的真实文件、也不联网,放心粘贴。
10.1 操作系统接口
os 是和操作系统打交道的入口:查/改工作目录、拼路径、读环境变量都靠它。真正「搬文件」的高层操作(复制、移动、删目录树)官方建议用更省心的 shutil,别在 os 里硬凑。
import os, shutil
print('当前工作目录:', os.getcwd())
p = os.path.join(os.getcwd(), 'demo', 'note.txt')
print('拼出来的路径:', p)
print('文件名 / 目录名:', os.path.basename(p), os.path.dirname(p))
print('HOME 在环境变量里吗:', 'HOME' in os.environ)
print('复制这类活交给 shutil:', hasattr(shutil, 'copyfile'))
村长划重点:拼路径一律用
os.path.join(),别自己拿+拼字符串——Windows 用反斜杠、macOS/Linux 用正斜杠,手写必翻车。另外少用os.system()去调 shell 命令,它既不好拿返回值又有注入隐患,现代代码请用subprocess.run()。
10.2 文件通配符
glob 用 shell 那套通配符从目录里筛文件名,*.py、data??.csv 都认识。它只负责「按名字找文件」,不做递归(要递归用 ** 配 recursive=True)。
import glob, os, tempfile
with tempfile.TemporaryDirectory() as d: # 建个临时目录,绝不碰你的硬盘
for name in ['a.py', 'b.py', 'note.txt', 'data01.csv']:
open(os.path.join(d, name), 'w', encoding='utf-8').close()
py = glob.glob(os.path.join(d, '*.py'))
print('所有 .py:', sorted(os.path.basename(x) for x in py))
print('data 开头两位数字:',
[os.path.basename(x) for x in glob.glob(os.path.join(d, 'data??.csv'))])
10.3 命令行参数
脚本后面跟的参数,Python 全塞在 sys.argv 这个列表里。记住 第 0 个是脚本自己的名字,真正的参数从 sys.argv[1] 开始。
import sys
print('脚本名 argv[0]:', sys.argv[0])
print('用户传的参数 argv[1:]:', sys.argv[1:])
在终端里跑 python demo.py 你好 42 时,sys.argv 长这样(脚本里可以直接 for arg in sys.argv[1:] 遍历):
['demo.py', '你好', '42']
村长划重点:
sys.argv[0]是脚本名、不是第一个参数,写循环时从下标 1 开始,这是新手最常差一位的地方。参数一多就别手搓解析了,标准库的argparse能自动生成-h帮助和类型转换。
10.4 错误输出重定向与程序终止
sys.stderr 是专门写「错误/警告」的通道,和 print 走的 sys.stdout 分开,这样用户 python demo.py > out.txt 时警告仍会显示在屏幕上。sys.exit() 则抛一个 SystemExit 来结束程序,可以带整数退出码或一句消息。
import sys
sys.stderr.write('这条走标准错误:参数不太对\n')
print('这条走标准输出')
try:
sys.exit('参数不合法,就此打住') # 也可写 sys.exit(2) 表示退出码
except SystemExit as exc:
print('捕获到 SystemExit,消息 / 码 =', exc.code)
sys.exit() 之所以能被 try 接住,正说明「异常也是类」;正常脚本里不用接,让它直接终止就行。
10.5 字符串模式匹配
re 提供正则表达式:findall 找全部、sub 做替换、match/search 做匹配。写模式时前面加 r(原始字符串),反斜杠就不用写两遍了。
import re
print(re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest'))
# ['foot', 'fell', 'fastest']
print(re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat'))
# cat in the hat —— 把重复的单词压成一个
s = '村长不好奇 2026 年,Python 3.13'
print('所有数字:', re.findall(r'\d+', s))
10.6 数学
math 管确定性的数学(三角、对数、取整、常量),random 管随机(抽签、洗牌、抽样),statistics 管描述统计(均值、中位数、方差)。三兄弟各管一摊,别混着找。
import math, random, statistics
print('cos(pi/4) =', round(math.cos(math.pi / 4), 4), '| log2(1024) =', math.log(1024, 2))
print('随机挑一个:', random.choice(['苹果', '香蕉', '梨']))
print('不重复抽 5 个:', random.sample(range(10), 5))
data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
print('均值 / 中位数 / 标准差:',
statistics.mean(data), statistics.median(data), round(statistics.stdev(data), 3))
10.7 互联网访问
urllib.request 负责按 URL 取数据,urllib.parse 负责拼查询串;发邮件则有 email(构造邮件)和 smtplib(投递)。本教程的示例只构造、不发送,避免真去联网。
from urllib.request import Request
from urllib.parse import urlencode
from email.message import EmailMessage
params = urlencode({'q': '不好奇编程'}).encode('utf-8')
req = Request('https://docs.python.org/3/search', data=params,
headers={'User-Agent': 'Mozilla/5.0'})
print('构造好的 URL:', req.full_url)
print('带 data 时的方法:', req.get_method()) # POST
# 真正联网时才打开(本教程不执行):
# from urllib.request import urlopen
# with urlopen(req, timeout=5) as r:
# html = r.read().decode('utf-8')
msg = EmailMessage()
msg['Subject'] = '标准库构造的邮件'
msg.set_content('这是正文,用 email 模块拼出来的。')
print('邮件主题 / 正文类型:', msg['Subject'], msg.get_content_type())
# 投递才需要连服务器(不执行):
# with smtplib.SMTP('localhost') as s:
# s.send_message(msg)
村长划重点:网络请求一定要设
timeout,否则对方不响应时程序会一直挂着;urlopen记得配with让它自动关连接。教学和单元测试里别真发请求——把请求对象构造出来、或者用本地假数据替代,既快又不会把别人的服务刷一遍。
10.8 日期与时间
datetime 里的 date 管年月日、datetime 管年月日时分秒、timedelta 管「一段时间」,strftime 负责按格式输出字符串。日期能直接加减 timedelta,不用自己算闰年。
from datetime import date, datetime, timedelta
now = datetime.now()
print('现在:', now.strftime('%Y-%m-%d %H:%M'))
d = date(2026, 11, 8)
print(d, '| 星期几(0=周一):', d.weekday(), '|', d.strftime('%A'))
print('十天后:', d + timedelta(days=10))
print('两个日期相差:', (date(2026, 12, 25) - d).days, '天')
10.9 数据压缩
zlib 做内存里字节串的压缩/解压,gzip、bz2、lzma、zipfile、tarfile 则处理对应格式的文件。压缩对重复多的文本特别有效,对已经压过的图片往往没用。
import zlib, gzip, io
s = b'witch which has which witches wrist watch ' * 10
print('原始字节数:', len(s))
packed = zlib.compress(s)
print('zlib 压缩后:', len(packed), '| 解压一致:', zlib.decompress(packed) == s)
buf = io.BytesIO() # 拿内存当文件用,不落盘
with gzip.GzipFile(fileobj=buf, mode='wb') as f:
f.write(s)
print('gzip 写出字节数:', len(buf.getvalue()))
10.10 性能测量
「谁更快」别靠掐表感觉,用 timeit:它会把代码重复跑很多次取稳定耗时,自动关掉垃圾回收以减少干扰。命令行里也能直接 python -m timeit "..."。
import timeit
print('列表推导:', round(timeit.timeit('[x**2 for x in range(100)]', number=1000), 4))
print('生成器转 list:', round(timeit.timeit('list(x**2 for x in range(100))', number=1000), 4))
print('重复 3 轮取最好:',
[round(t, 4) for t in timeit.repeat('sum(range(100))', number=1000, repeat=3)])
timeit.timeit 的第二个可选项是 setup 字符串,用来放「不计入计时」的准备工作(比如 import)。
10.11 质量控制
doctest 把文档字符串里 >>> 后面的「交互式示例」当成测试用例跑;unittest 则是标准库自带的完整测试框架,用类组织用例、assert* 系列方法断言。写小函数时 doctest 最省事,项目成型后上 unittest。
import doctest
def average(values):
"""返回算术平均值。
>>> average([20, 30, 70])
40.0
>>> average([1, 2, 3, 4])
2.5
"""
return sum(values) / len(values)
print('doctest:', doctest.testmod(verbose=False)) # TestResults(failed=0, attempted=2)
import unittest
class TestAverage(unittest.TestCase):
def test_ints(self):
self.assertEqual(average([20, 30, 70]), 40.0)
def test_empty(self):
with self.assertRaises(ZeroDivisionError):
average([])
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestAverage)
result = unittest.TextTestRunner(verbosity=0).run(suite)
print('共跑', result.testsRun, '个用例,失败', len(result.failures))
村长划重点:doctest 的
>>>后面必须留一个空格,期望输出要另起一行且顶格,差一个空格就匹配不上。另一个坑是浮点数:别写>>> 0.1 + 0.2然后期望0.3,二进制浮点根本不等于它,doctest 会稳稳地失败。
10.12 电池已包含
「Batteries Included」是 Python 的一句老口号:装好解释器,JSON、CSV、SQLite 数据库、HTTP 服务、小数运算、路径处理……全都自带,不用额外装任何东西。官方用这一节收尾,意思是「动手写之前先翻翻标准库索引」。
import json, sqlite3, csv, io
print(json.dumps({'教程': '不好奇编程'}, ensure_ascii=False))
con = sqlite3.connect(':memory:') # 内存数据库,开箱即用
con.execute('create table t (name text)')
con.execute('insert into t values (?)', ('村长',))
print('SQLite 查出来:', con.execute('select * from t').fetchall())
buf = io.StringIO()
csv.writer(buf).writerow(['名字', '章节'])
print('CSV 拼好:', repr(buf.getvalue()))
标准库不是「什么都最好」,第三方库在性能和体验上常常更强;但「不用装、不用管依赖、跟着 Python 版本走」这三点,让它永远是第一选择。
本章小结
os管系统与路径(拼路径用os.path.join),shutil管文件搬运;glob按通配符筛文件名。sys.argv[0]是脚本名、参数从 1 开始;sys.stderr与sys.stdout分流,sys.exit()抛SystemExit终止程序。re用原始字符串写模式;数学三兄弟math/random/statistics分工明确;datetime配timedelta做日期加减。zlib/gzip做压缩,timeit客观比性能,doctest与unittest把示例和断言变成自动测试。- 「电池已包含」是选型的默认答案:JSON、CSV、SQLite 等随解释器自带,造轮子前先查标准库。