(十六)处理常见任务的标准库
1. Counter 统计次数
第二章用 counts[title] = counts.get(title, 0) + 1 逐项统计。collections.Counter 把这项工作封装好了:
# Counter 读取书名,按同名次数建立计数;没有出现的键读作 0。
from collections import Counter
counts = Counter(["Python", "NumPy", "Python", "Pygame"])
print(counts["Python"], counts["Django"])
print(counts.most_common(2))
# 需要指定同次数的顺序时,继续用明确的排序键。
print(sorted(counts.items(), key=lambda item: (-item[1], item[0])))得到 2 0、[('Python', 2), ('NumPy', 1)],以及 [('Python', 2), ('NumPy', 1), ('Pygame', 1)]。most_common(2) 按次数取前两项,同次数按首次出现顺序排列;最后的排序则明确要求同次数按名称升序。
2. defaultdict 为新键建立分组
# 传入 list 这个可调用对象,首次用 [] 访问新键时由它创建空列表。
from collections import defaultdict
groups = defaultdict(list)
for title, minutes in [("Python", 20), ("NumPy", 35), ("Python", 45)]:
groups[title].append(minutes)
print(dict(groups))得到 {'Python': [20, 45], 'NumPy': [35]}。第一次读取 groups["Python"] 时,defaultdict 调用 list(),把新列表放进该键,再追加 20。传入的是 list,写成 list() 会先得到列表对象,而这里需要一个能被调用的默认工厂。
这种自动建立发生在下标读取 groups[key] 中;groups.get(key) 仍遵循普通字典的读取方式,不会为缺失键调用工厂。要合并所有同名记录,用字典式分组;要分开连续的同名片段,用前面的 groupby。
3. deque 从两端处理数据
# deque 支持在两端加入与取出,这里按加入顺序处理任务。
from collections import deque
tasks = deque(["整理语法", "完成练习"])
tasks.append("复盘错误")
print(tasks.popleft())
print(list(tasks))
# maxlen=3 保留最近三项,满后从另一端移除旧项。
recent = deque([20, 35, 45], maxlen=3)
recent.append(10)
print(list(recent))输出“整理语法”、['完成练习', '复盘错误']、[35, 45, 10]。列表从头删除需要移动后面的引用;双端队列将两端加入、取出的常见工作量保持为 ,适合排队处理和保留近期记录。随机读取中间位置时,它与列表按下标访问的成本不同。collections 容器工具
4. datetime 处理日期、时间与时区
# date 表示日历日期,timedelta 表示时间间隔。
from datetime import date, timedelta
started = date.fromisoformat("2026-10-19")
deadline = started + timedelta(days=7)
print(deadline.isoformat())
print((deadline - started).days)
# total_seconds 计算完整间隔;seconds 只是扣除整天后的秒数部分。
gap = timedelta(days=1, minutes=5)
print(gap.total_seconds(), gap.seconds)输出 2026-10-26、7、86700.0 300。fromisoformat 从 ISO 格式字符串建立日期对象,isoformat 转回这种格式。直接用日期运算,不需要自己按月份拆算天数。
记录一个具体时刻时,还要带上时区信息:
# 输入中的 +08:00 表示比 UTC 快八小时;转换后仍表示同一时刻。
from datetime import datetime, timezone
local = datetime.fromisoformat("2026-10-19T08:30:00+08:00")
utc_time = local.astimezone(timezone.utc)
print(local.isoformat())
print(utc_time.isoformat())分别输出 2026-10-19T08:30:00+08:00 和 2026-10-19T00:30:00+00:00。datetime 同时保存日期与时间,timezone.utc 提供 UTC 时区,astimezone 换一种时区表示相同时刻。带时区信息的值称为 aware datetime;没有时区信息的值称为 naive datetime。把两者直接相减会报错,因为后者没有说明时间属于哪一个时区。日期与时间
5. re 提取和检查有结构的文本
数据行采用 书名:分钟数 的形式时,第二章的 split 已经能完成简单切分。若还需要检查分钟部分全是数字,可以用正则表达式描述字符结构:
# compile 把模式建立成可复用对象;fullmatch 要求整行符合模式。
import re
pattern = re.compile(r"(.+):([0-9]+)")
for line in ["Python 入门:45", "Python 入门:很多"]:
matched = pattern.fullmatch(line)
if matched is None:
print("记录格式错误")
continue
# 两对圆括号保存两个分组,分别取出书名和数字文本。
title = matched.group(1)
minutes = int(matched.group(2))
print(title, minutes)
# findall 查找所有数字片段,sub 将连续空白统一替换为一个空格。
print(re.findall(r"[0-9]+", "阅读20分钟,休息5分钟"))
print(re.sub(r"\s+", " ", "Python 入门"))输出“Python 入门 45”“记录格式错误”、['20', '5'] 和“Python 入门”。r"..." 是原始字符串写法,反斜杠由正则规则解释,例如 r"\s" 表示正则中的空白字符类;原始字符串并不会关闭正则本身的特殊符号。
| 模式片段或操作 | 本例中的动作 |
|---|---|
.、+ |
匹配换行以外的字符,并重复一次或多次 |
[0-9]+ |
取得一串 ASCII 数字 |
(...) |
保存可以通过 group 取回的分组 |
\s+ |
匹配连续空白 |
fullmatch |
整段文本必须符合模式 |
search、findall |
在文本中查找首个匹配,或取得全部匹配结果 |
这里的“整行匹配成功”先给出匹配对象,matched is None 则表示没有找到。访问分组前先处理未匹配的情况。HTML、JSON 这样的格式还有自己的结构与解析器,爬虫和文件章节会用对应工具读取。正则表达式