39. 必备标准库模块
Python 的标准库(standard library)是一组随 Python 一起内置的模块——你不需要额外安装任何东西就能使用它们。这些模块为常见编程任务提供了强大的工具:生成随机数、处理日期与时间、与其他程序交换数据,以及使用超越基础列表(list)和字典(dictionary)的专用数据结构。
在本章中,我们将探索五个在真实世界 Python 编程中你会经常使用的必备标准库模块。
39.1) 使用 random 生成随机性
random 模块提供用于生成随机数并进行随机选择的函数。这对仿真、游戏、测试、数据抽样,以及任何需要不可预测行为的场景都很有用。
39.1.1) 使用 randint() 生成随机整数
randint() 函数会在两个值之间生成一个随机整数,并且两端都是包含(inclusive)的:
import random
# 模拟掷一个六面骰子
die_roll = random.randint(1, 6)
print(f"You rolled: {die_roll}") # Output: You rolled: 4 (varies each run)
# 生成一个介于 18 到 65 的随机年龄
age = random.randint(18, 65)
print(f"Random age: {age}") # Output: Random age: 42 (varies)注意,起始值与结束值都被包含在可能的结果中。randint(1, 6) 可能返回 1、2、3、4、5 或 6——六个值都可能出现。
下面是一个模拟多次掷骰子的实用示例:
import random
# 模拟掷两个骰子并计算它们的和
die1 = random.randint(1, 6)
die2 = random.randint(1, 6)
total = die1 + die2
print(f"Die 1: {die1}") # Output: Die 1: 3 (varies)
print(f"Die 2: {die2}") # Output: Die 2: 5 (varies)
print(f"Total: {total}") # Output: Total: 8 (varies)
if total == 7:
print("Lucky seven!")
elif total == 2 or total == 12:
print("Snake eyes or boxcars!")为什么两端都包含:这让 randint() 在常见用例中更直观。当你想要一个从 1 到 6 的数字(比如骰子点数),你写 randint(1, 6),并且 1 和 6 都是可能的结果。
39.1.2) 生成随机浮点数
对于随机小数,使用 random()(返回 0.0 到 1.0 之间的 float)或 uniform()(返回两个指定值之间的 float):
import random
# 生成一个介于 0.0 与 1.0 之间的随机浮点数(包含 0.0,不包含 1.0)
probability = random.random()
print(f"Random probability: {probability:.4f}") # Output: Random probability: 0.7284 (varies)
# 生成一个介于 15.0 到 30.0 度的随机温度
temperature = random.uniform(15.0, 30.0)
print(f"Temperature: {temperature:.2f}°C") # Output: Temperature: 23.47°C (varies)
# 生成一个介于 $10.00 与 $99.99 的随机价格
price = random.uniform(10.0, 99.99)
print(f"Price: ${price:.2f}") # Output: Price: $45.67 (varies)当你需要一个概率值或百分比时,random() 函数很有用。当你需要某个特定范围内的随机小数时,uniform() 函数更合适。
39.1.3) 使用 choice() 进行随机选择
choice() 函数会从一个序列(列表、元组或字符串)中随机选择一个元素:
import random
# 随机选择一种颜色
colors = ["red", "blue", "green", "yellow", "purple"]
selected_color = random.choice(colors)
print(f"Selected color: {selected_color}") # Output: Selected color: green (varies)
# 从参与者中随机选出一位获胜者
participants = ["Alice", "Bob", "Charlie", "Diana"]
winner = random.choice(participants)
print(f"The winner is: {winner}") # Output: The winner is: Bob (varies)
# 从字符串中随机选择一个字符
vowels = "aeiou"
random_vowel = random.choice(vowels)
print(f"Random vowel: {random_vowel}") # Output: Random vowel: i (varies)这对游戏、随机抽样或选择随机测试数据特别有用。序列中的每个元素都有相同的被选中概率。
下面是一个更复杂的示例,用来模拟一个简单的问答游戏:
import random
# 测验题目与答案
questions = [
("What is 2 + 2?", "4"),
("What is the capital of France?", "Paris"),
("What color is the sky?", "blue")
]
# 随机选择一个问题
question, correct_answer = random.choice(questions)
print(f"Question: {question}")
user_answer = input("Your answer: ")
if user_answer.lower() == correct_answer.lower():
print("Correct!")
else:
print(f"Wrong! The answer was: {correct_answer}")39.1.4) 使用 sample() 选择多个随机项
当你需要从序列中选择多个不重复的项目时,使用 sample()。这就像从一副牌里抽牌且不放回——一旦某个项被选中,它就不会再次被选中:
import random
# 为小组项目随机选择 3 名学生
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
group = random.sample(students, 3)
print(f"Group members: {group}") # Output: Group members: ['Diana', 'Alice', 'Frank'] (varies)
# 从 1 到 50 抽取 5 个彩票号码(无重复)
lottery_numbers = random.sample(range(1, 51), 5)
lottery_numbers.sort() # Sort for display
print(f"Lottery numbers: {lottery_numbers}") # Output: Lottery numbers: [7, 15, 23, 38, 49] (varies)sample() 的第二个参数指定要选择多少个项目。这个数量必须小于或等于序列的长度——你不能选择超过可用数量的项目。
39.1.5) 使用 shuffle() 打乱序列
shuffle() 函数会原地(in place)随机重排列表中的元素(修改原始列表):
import random
# 洗牌一副扑克牌
cards = ["A♠", "K♠", "Q♠", "J♠", "10♠", "9♠", "8♠", "7♠"]
print(f"Original: {cards}")
random.shuffle(cards)
print(f"Shuffled: {cards}") # Output: Shuffled: ['Q♠', '7♠', 'A♠', '10♠', '9♠', 'J♠', 'K♠', '8♠'] (varies)
# 打乱测验题目以实现随机顺序
questions = ["Question 1", "Question 2", "Question 3", "Question 4"]
random.shuffle(questions)
print(f"Randomized order: {questions}") # Output: Randomized order: ['Question 3', 'Question 1', 'Question 4', 'Question 2'] (varies)39.2) 处理日期与时间
datetime 模块提供用于处理日期、时间以及时间间隔的类。这对日程安排、日志记录、计算持续时间,以及任何需要跟踪事件发生时间的应用都至关重要。
39.2.1) 获取当前日期与时间
datetime 类表示一个特定的时间点,同时包含日期与时间组件:
from datetime import datetime
# 获取当前日期与时间
now = datetime.now()
print(f"Current datetime: {now}")
# Output: Current datetime: 2026-01-02 14:30:45.123456
# 访问各个组件
print(f"Year: {now.year}") # Output: Year: 2026
print(f"Month: {now.month}") # Output: Month: 1
print(f"Day: {now.day}") # Output: Day: 2
print(f"Hour: {now.hour}") # Output: Hour: 14
print(f"Minute: {now.minute}") # Output: Minute: 30
print(f"Second: {now.second}") # Output: Second: 45如果只需要日期(不包含时间),使用 date 类:
from datetime import date
# 获取今天的日期
today = date.today()
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Year: {today.year}") # Output: Year: 2026
print(f"Month: {today.month}") # Output: Month: 1
print(f"Day: {today.day}") # Output: Day: 239.2.2) 创建特定日期与时间
你可以为特定时间点创建 datetime 与 date 对象:
from datetime import datetime, date
# 创建一个特定日期
birthday = date(1995, 7, 15)
print(f"Birthday: {birthday}") # Output: Birthday: 1995-07-15
# 创建一个特定 datetime
meeting = datetime(2026, 3, 15, 14, 30) # March 15, 2026 at 2:30 PM
print(f"Meeting: {meeting}") # Output: Meeting: 2026-03-15 14:30:00这对于表示截止日期、预约、历史日期,或任何固定时间点都很有用:
from datetime import date
# 项目中的重要日期
project_start = date(2026, 1, 15)
project_end = date(2026, 6, 30)
print(f"Project duration: {project_start} to {project_end}")
# Output: Project duration: 2026-01-15 to 2026-06-3039.2.3) 使用 timedelta 计算时间差
timedelta 类表示一个持续时间——两个日期或时间之间的差值。你可以用它来计算已经过去了多少时间,或对日期进行加/减时间:
from datetime import date, timedelta
# 计算年龄
birth_date = date(1995, 7, 15)
today = date(2026, 1, 2)
age_delta = today - birth_date
print(f"Days since birth: {age_delta.days}") # Output: Days since birth: 11128
print(f"Years (approximate): {age_delta.days // 365}") # Output: Years (approximate): 30当你用一个日期减去另一个日期时,会得到一个 timedelta 对象。days 属性会告诉你该持续时间包含的天数。
你也可以直接创建 timedelta 对象来表示特定持续时间:
from datetime import date, timedelta
# 给日期加上天数
today = date(2026, 1, 2)
one_week = timedelta(days=7)
next_week = today + one_week
print(f"Today: {today}") # Output: Today: 2026-01-02
print(f"Next week: {next_week}") # Output: Next week: 2026-01-09
# 从日期中减去天数
thirty_days_ago = today - timedelta(days=30)
print(f"30 days ago: {thirty_days_ago}") # Output: 30 days ago: 2025-12-03timedelta 可以表示天、秒、微秒、毫秒、分钟、小时和周:
from datetime import datetime, timedelta
# 计算一个截止时间
now = datetime(2026, 1, 2, 14, 30)
deadline = now + timedelta(hours=48, minutes=30)
print(f"Current time: {now}") # Output: Current time: 2026-01-02 14:30:00
print(f"Deadline: {deadline}") # Output: Deadline: 2026-01-04 15:00:00
# 计算剩余时间
time_left = deadline - now
print(f"Hours remaining: {time_left.total_seconds() / 3600}") # Output: Hours remaining: 48.5total_seconds() 方法会把整个持续时间转换为秒,然后你可以再把它换算成小时、分钟或任何其他单位。
下面是一个计算项目里程碑的实用示例:
from datetime import date, timedelta
# 项目规划
project_start = date(2026, 1, 15)
sprint_duration = timedelta(weeks=2)
sprint_1_end = project_start + sprint_duration
sprint_2_end = sprint_1_end + sprint_duration
sprint_3_end = sprint_2_end + sprint_duration
print(f"Sprint 1: {project_start} to {sprint_1_end}")
# Output: Sprint 1: 2026-01-15 to 2026-01-29
print(f"Sprint 2: {sprint_1_end} to {sprint_2_end}")
# Output: Sprint 2: 2026-01-29 to 2026-02-12
print(f"Sprint 3: {sprint_2_end} to {sprint_3_end}")
# Output: Sprint 3: 2026-02-12 to 2026-02-2639.2.4) 比较日期与时间
date 与 datetime 对象可以使用标准比较运算符进行比较:
from datetime import date
# 比较日期
date1 = date(2026, 1, 15)
date2 = date(2026, 2, 20)
date3 = date(2026, 1, 15)
print(date1 < date2) # Output: True
print(date1 == date3) # Output: True
print(date2 > date1) # Output: True这对于检查截止日期、验证日期范围以及对日期进行排序都很有用:
from datetime import date
# 检查某个日期是否在过去
event_date = date(2025, 12, 25)
today = date(2026, 1, 2)
if event_date < today:
print("This event has already passed") # Output: This event has already passed
else:
print("This event is upcoming")
# 对日期列表排序
important_dates = [
date(2026, 3, 15),
date(2026, 1, 10),
date(2026, 2, 28)
]
important_dates.sort()
print("Dates in order:") # Output: Dates in order:
for d in important_dates:
print(f" {d}")
# Output:
# 2026-01-10
# 2026-02-28
# 2026-03-1539.2.5) 使用 strftime() 格式化日期与时间
strftime() 方法(string format time)会把日期与时间转换为格式化的字符串。你用特殊代码来指定格式:
from datetime import datetime
now = datetime(2026, 1, 2, 14, 30, 45)
# 常见日期格式
print(now.strftime("%Y-%m-%d")) # Output: 2026-01-02
print(now.strftime("%m/%d/%Y")) # Output: 01/02/2026
print(now.strftime("%B %d, %Y")) # Output: January 02, 2026
print(now.strftime("%A, %B %d, %Y")) # Output: Friday, January 02, 2026
# 常见时间格式
print(now.strftime("%H:%M:%S")) # Output: 14:30:45
print(now.strftime("%I:%M %p")) # Output: 02:30 PM
# 组合格式
print(now.strftime("%Y-%m-%d %H:%M:%S")) # Output: 2026-01-02 14:30:45
print(now.strftime("%B %d, %Y at %I:%M %p")) # Output: January 02, 2026 at 02:30 PM常见格式代码:
| Code | 描述 | 示例 |
|---|---|---|
%Y | 带世纪的年份 | 2026 |
%m | 以零填充的月份数字 (01-12) | 01 |
%d | 以零填充的日期数字 (01-31) | 02 |
%B | 月份全称 | January |
%b | 月份简称 | Jan |
%A | 星期全称 | Friday |
%a | 星期简称 | Fri |
%H | 24 小时制小时 (00-23) | 14 |
%I | 12 小时制小时 (01-12) | 02 |
%M | 分钟 (00-59) | 30 |
%S | 秒 (00-59) | 45 |
%p | AM/PM | PM |
下面是一个创建日志条目的实用示例:
from datetime import datetime
def log_event(message):
"""记录带时间戳的事件"""
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] {message}")
log_event("User logged in")
# Output: [2026-01-02 14:30:45] User logged in
log_event("File uploaded successfully")
# Output: [2026-01-02 14:30:45] File uploaded successfully39.2.6) 使用 strptime() 从字符串解析日期
strptime() 函数(string parse time)会把格式化字符串转换回 datetime 对象。你指定同样的格式代码来告诉 Python 如何解释这个字符串:
from datetime import datetime
# 解析不同的日期格式
date_str1 = "2026-01-15"
date1 = datetime.strptime(date_str1, "%Y-%m-%d")
print(f"Parsed: {date1}") # Output: Parsed: 2026-01-15 00:00:00
date_str2 = "January 15, 2026"
date2 = datetime.strptime(date_str2, "%B %d, %Y")
print(f"Parsed: {date2}") # Output: Parsed: 2026-01-15 00:00:00
# 解析带时间的 datetime
datetime_str = "2026-01-15 14:30:00"
dt = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
print(f"Parsed: {dt}") # Output: Parsed: 2026-01-15 14:30:00当你从文件、用户输入或外部数据源读取日期时,这一点至关重要:
from datetime import datetime
# 解析用户输入
user_input = "03/15/2026"
try:
event_date = datetime.strptime(user_input, "%m/%d/%Y")
print(f"Event scheduled for: {event_date.strftime('%B %d, %Y')}")
# Output: Event scheduled for: March 15, 2026
except ValueError:
print("Invalid date format. Please use MM/DD/YYYY")重要:格式字符串必须与输入字符串完全匹配,否则你会得到 ValueError:
from datetime import datetime
# 这会失败 - 格式不匹配
try:
datetime.strptime("2026-01-15", "%m/%d/%Y") # Wrong format
except ValueError as e:
print(f"Error: {e}")
# Output: Error: time data '2026-01-15' does not match format '%m/%d/%Y'39.3) 读取与写入 JSON 数据
JSON(JavaScript Object Notation)是一种用于存储与交换结构化数据的文本格式。它是 Web API、配置文件以及程序间数据交换中最常见的格式。Python 的 json 模块让 Python 数据结构与 JSON 文本之间的转换变得很容易。
39.3.1) 理解 JSON 结构
JSON 看起来类似 Python 字典和列表,但也有一些不同:
JSON 支持这些数据类型:
- 对象(类似 Python 字典):
{"name": "Alice", "age": 30} - 数组(类似 Python 列表):
[1, 2, 3, 4] - 字符串:
"hello"(必须使用双引号) - 数字:
42、3.14 - 布尔值:
true、false(小写) - 空值:
null(类似 Python 的None)
与 Python 的关键差异:
- JSON 使用
true/false/null,而不是 Python 的True/False/None - JSON 字符串必须使用双引号(
"text"),不能用单引号 - JSON 不直接支持元组、集合或自定义对象
下面是 JSON 数据的样子:
{
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": true,
"scores": [85, 92, 78, 95],
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "12345"
}
}注意: 这是一段纯 JSON 文本,不是 Python 代码。注意小写的 true 以及双引号的使用。
39.3.2) 使用 dumps() 将 Python 数据转换为 JSON
dumps() 函数(dump string)会把 Python 数据结构转换为 JSON 格式的字符串:
import json
student = {
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"is_active": True,
"scores": [85, 92, 78, 95]
}
# 将字典转换为 JSON
json_string = json.dumps(student)
print(json_string)
# Output: {"name": "Alice Johnson", "age": 30, "email": "alice@example.com", "is_active": true, "scores": [85, 92, 78, 95]}
print(type(json_string)) # Output: <class 'str'>注意输出中 Python 的 True 是如何变成 JSON 的 true 的。dumps() 函数会自动处理这些转换。
如果想要更易读的输出,使用 indent 参数:
import json
student = {
"name": "Alice Johnson",
"age": 30,
"scores": [85, 92, 78, 95]
}
# 使用缩进进行美化输出
json_string = json.dumps(student, indent=2)
print(json_string)
# Output:
# {
# "name": "Alice Johnson",
# "age": 30,
# "scores": [
# 85,
# 92,
# 78,
# 95
# ]
# }indent 参数指定每一级缩进使用多少个空格。这会让 JSON 更容易阅读,尤其是面对复杂的嵌套结构时。
39.3.3) 使用 loads() 将 JSON 转换为 Python 数据
loads() 函数(load string)会把 JSON 格式字符串转换回 Python 数据结构:
import json
# JSON 字符串(比如你可能从 Web API 接收到的)
json_string = '{"name": "Bob Smith", "age": 25, "scores": [90, 88, 92]}'
# 转换为 Python 字典
student = json.loads(json_string)
print(student) # Output: {'name': 'Bob Smith', 'age': 25, 'scores': [90, 88, 92]}
print(type(student)) # Output: <class 'dict'>
# 像任何 Python 字典一样访问数据
print(f"Name: {student['name']}") # Output: Name: Bob Smith
print(f"Average score: {sum(student['scores']) / len(student['scores'])}")
# Output: Average score: 90.0JSON 的 true、false 和 null 会自动转换为 Python 的 True、False 和 None:
import json
json_string = '{"active": true, "verified": false, "middle_name": null}'
data = json.loads(json_string)
print(data) # Output: {'active': True, 'verified': False, 'middle_name': None}
print(type(data["active"])) # Output: <class 'bool'>
print(type(data["middle_name"])) # Output: <class 'NoneType'>39.3.4) 使用 dump() 将 JSON 写入文件
dump() 函数会把 Python 数据直接以 JSON 格式写入文件:
import json
# 学生记录
students = [
{"name": "Alice", "age": 20, "gpa": 3.8},
{"name": "Bob", "age": 22, "gpa": 3.5},
{"name": "Charlie", "age": 21, "gpa": 3.9}
]
# 写入 JSON 文件
with open("students.json", "w") as file:
json.dump(students, file, indent=2)
print("Data written to students.json")
# Output: Data written to students.json运行这段代码后,文件 students.json 的内容为:
[
{
"name": "Alice",
"age": 20,
"gpa": 3.8
},
{
"name": "Bob",
"age": 22,
"gpa": 3.5
},
{
"name": "Charlie",
"age": 21,
"gpa": 3.9
}
]为什么用 dump() 而不是 dumps()? dump() 会直接写入文件,这比先转换成字符串再写入字符串更高效。处理文件时用 dump(),当你需要把 JSON 作为字符串(例如通过网络发送)时用 dumps()。
39.3.5) 使用 load() 从文件读取 JSON
load() 函数从文件读取 JSON 数据并把它转换为 Python 数据结构:
import json
# 从我们之前创建的 JSON 文件读取
with open("students.json", "r") as file:
students = json.load(file)
print(f"Loaded {len(students)} students") # Output: Loaded 3 students
# 使用数据
for student in students:
print(f"{student['name']}: GPA {student['gpa']}")
# Output:
# Alice: GPA 3.8
# Bob: GPA 3.5
# Charlie: GPA 3.939.3.6) 处理 JSON 错误
处理 JSON 时,你可能会遇到无效数据。务必处理潜在错误:
import json
# 无效 JSON - 缺少结束引号
invalid_json = '{"name": "Alice", "age": 30'
try:
data = json.loads(invalid_json)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")
# Output: Invalid JSON: Expecting ',' delimiter: line 1 column 28 (char 27)这在从外部来源(文件、Web API、用户输入)读取 JSON 时尤其重要,因为你无法保证数据一定有效:
import json
def load_config(filename):
"""从 JSON 文件加载配置,并进行错误处理"""
try:
with open(filename, "r") as file:
config = json.load(file)
return config
except FileNotFoundError:
print(f"Config file '{filename}' not found")
return None
except json.JSONDecodeError as e:
print(f"Invalid JSON in '{filename}': {e}")
return None
# 尝试加载配置
config = load_config("config.json")
if config:
print(f"Configuration loaded: {config}")
else:
print("Using default configuration")39.3.7) JSON 实战示例:保存与加载应用状态
下面是一个完整示例,展示如何保存与加载应用数据:
import json
def save_game_state(filename, player_data):
"""将游戏存档保存到 JSON 文件"""
with open(filename, "w") as file:
json.dump(player_data, file, indent=2)
print(f"Game saved to {filename}")
def load_game_state(filename):
"""从 JSON 文件加载游戏存档"""
try:
with open(filename, "r") as file:
player_data = json.load(file)
print(f"Game loaded from {filename}")
return player_data
except FileNotFoundError:
print("No saved game found")
return None
# 游戏数据
player = {
"name": "Hero",
"level": 5,
"health": 85,
"inventory": ["sword", "shield", "potion"],
"position": {"x": 10, "y": 20}
}
# 保存游戏
save_game_state("savegame.json", player)
# Output: Game saved to savegame.json
# 之后加载游戏
loaded_player = load_game_state("savegame.json")
# Output: Game loaded from savegame.json
if loaded_player:
print(f"Welcome back, {loaded_player['name']}!")
print(f"Level: {loaded_player['level']}, Health: {loaded_player['health']}")
# Output:
# Welcome back, Hero!
# Level: 5, Health: 8539.4) collections 中的实用容器
collections 模块提供专用容器类型,用额外功能扩展了 Python 内置容器(列表、字典、集合)。这些容器比使用基础数据结构更优雅地解决常见问题。
39.4.1) 使用 Counter 统计项目数量
Counter 类用于统计可哈希对象。它是字典的子类,将项目作为键,并将其计数作为值。
Counter 可接受的输入:
- 任意可迭代对象(列表、字符串、元组等)
- 另一个带计数的字典
- 带计数的关键字参数
Counter 存储的内容:
- 一个字典,其中键是项目,值是它们的计数
- 示例:
Counter(['a', 'b', 'a'])存储{'a': 2, 'b': 1}
相对普通字典的关键优势:
- 对缺失键返回 0,而不是抛出
KeyError - 提供
most_common()等计数专用方法 - 支持计数器之间的算术运算
基本用法
from collections import Counter
# 统计单词中的字母出现次数
word = "mississippi"
letter_counts = Counter(word)
print(letter_counts)
# Output: Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
# 像字典一样访问计数
print(f"Number of 'i's: {letter_counts['i']}")
# Output: Number of 'i's: 4
print(f"Number of 'z's: {letter_counts['z']}")
# Output: Number of 'z's: 0 (returns 0 for missing keys, no KeyError!)从不同来源创建 Counter
from collections import Counter
# 从列表创建
votes = ["Alice", "Bob", "Alice", "Charlie", "Alice", "Bob", "Alice"]
vote_counts = Counter(votes)
print(vote_counts)
# Output: Counter({'Alice': 4, 'Bob': 2, 'Charlie': 1})
# 从字符串创建(统计每个字符)
letter_counts = Counter("hello")
print(letter_counts)
# Output: Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
# 从字典创建
existing_counts = {'apple': 3, 'banana': 2}
fruit_counts = Counter(existing_counts)
print(fruit_counts)
# Output: Counter({'apple': 3, 'banana': 2})
# 从关键字参数创建
color_counts = Counter(red=5, blue=3, green=2)
print(color_counts)
# Output: Counter({'red': 5, 'blue': 3, 'green': 2})使用 most_common() 查找最常见项目
方法签名: most_common(n=None)
参数:
n(可选):要返回的最常见项目数量- 如果省略
n或为None,则返回所有项目
返回值:
- 一个由
(item, count)元组组成的列表 - 按计数排序,最高的在前
- 如果计数相同,则按首次出现的顺序排列
from collections import Counter
# 分析文本中的词频
text = "the quick brown fox jumps over the lazy dog the fox"
words = text.split()
word_counts = Counter(words)
# 获取最常见的 3 个单词
top_3 = word_counts.most_common(3)
print(top_3)
# Output: [('the', 3), ('fox', 2), ('quick', 1)]Counter 的算术运算
你可以对 Counter 对象进行加、减以及其他操作:
from collections import Counter
# 统计两组中的项目数量
group1 = Counter(["apple", "banana", "apple", "orange"])
print(group1)
# Output: Counter({'apple': 2, 'banana': 1, 'orange': 1})
group2 = Counter(["banana", "banana", "grape", "apple"])
print(group2)
# Output: Counter({'banana': 2, 'grape': 1, 'apple': 1})
# 将计数相加
combined = group1 + group2
print(combined)
# Output: Counter({'apple': 3, 'banana': 3, 'orange': 1, 'grape': 1})
# 相减计数(只保留正数结果)
difference = group1 - group2
print(difference)
# Output: Counter({'apple': 1, 'orange': 1})
# banana: 1 - 2 = -1 (negative, so excluded)
# grape: not in group1, so excluded实用示例:分析学生成绩
from collections import Counter
# 成绩分布
grades = ["A", "B", "A", "C", "B", "A", "B", "D", "A", "B", "C", "A"]
grade_counts = Counter(grades)
print(f"Total students: {len(grades)}")
# Output: Total students: 12
print("\nGrade Distribution:")
for grade, count in grade_counts.most_common():
percentage = (count / len(grades)) * 100
bar = "█" * count
print(f" {grade}: {count} students ({percentage:4.1f}%) {bar}")
# Output:
# Grade Distribution:
# A: 5 students (41.7%) █████
# B: 4 students (33.3%) ████
# C: 2 students (16.7%) ██
# D: 1 students ( 8.3%) █39.4.2) 使用 defaultdict 创建带默认值的字典
defaultdict 类是字典的子类,当你访问缺失键时,它会自动创建带默认值的条目。这消除了在使用前检查键是否存在的需求。
defaultdict 可接受的输入:
- 一个默认工厂(default factory)函数(必需):一个可调用对象,用于为缺失键返回默认值
- 普通
dict可接受的任何参数(键值对、另一个字典、关键字参数)
相对普通字典的关键优势:
- 使用前无需检查键是否存在
- 自动为缺失键初始化默认值
- 在分组、计数与累加操作中,代码更简洁、更易读
理解默认工厂
创建 defaultdict 时,你必须提供一个默认工厂——一个不接收参数、返回默认值的可调用对象(函数)。常见默认工厂包括:
int- 返回0(用于计数)list- 返回[](用于分组)set- 返回set()(用于收集唯一项目)str- 返回''(用于字符串拼接)lambda: value- 返回自定义默认值
from collections import defaultdict
# 不同的默认工厂
counts = defaultdict(int) # 缺失键返回 0
groups = defaultdict(list) # 缺失键返回 []
unique = defaultdict(set) # 缺失键返回 set()
custom = defaultdict(lambda: "N/A") # 缺失键返回 "N/A"
# 用缺失键进行测试
print(counts['missing']) # Output: 0
print(groups['missing']) # Output: []
print(unique['missing']) # Output: set()
print(custom['missing']) # Output: N/A基本用法:使用 defaultdict 计数
对比普通字典与 defaultdict 的计数写法:
from collections import defaultdict
word = "mississippi"
# 普通字典 - 需要检查键是否存在
regular_dict = {}
for letter in word:
if letter not in regular_dict:
regular_dict[letter] = 0
regular_dict[letter] += 1
print(regular_dict)
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}
# defaultdict - 自动创建带默认值的条目
letter_counts = defaultdict(int) # int() 返回 0
for letter in word:
letter_counts[letter] += 1 # 无需检查键是否存在!
print(dict(letter_counts))
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}工作原理:
- 当你第一次访问新字母的
letter_counts[letter]时,defaultdict会调用int()并返回0 - 随后会创建该键并赋值为
0,然后+= 1使其变为1 - 对已存在的键,它与普通字典行为一致
使用 defaultdict(list) 分组
一个常见用例是将项目按类别分组:
from collections import defaultdict
students = [
("Alice", "A"),
("Bob", "B"),
("Charlie", "A"),
("Diana", "C"),
("Eve", "B"),
("Frank", "A")
]
# 按成绩分组学生
# 使用 defaultdict - 干净且简单
students_by_grade = defaultdict(list)
for name, grade in students:
students_by_grade[grade].append(name)
print(dict(students_by_grade))
# Output: {'A': ['Alice', 'Charlie', 'Frank'], 'B': ['Bob', 'Eve'], 'C': ['Diana']}
# 访问一个尚不存在的成绩
print(students_by_grade["D"]) # Output: [] (empty list, not KeyError!)工作原理:
- 当你第一次访问新成绩的
students_by_grade[grade]时,defaultdict会调用list()并返回[] - 随后创建该键并赋值为空列表,然后
.append(name)添加第一个学生 - 对已存在的成绩,它只会向现有列表追加
从现有字典创建 defaultdict
你可以用已有数据初始化一个 defaultdict:
from collections import defaultdict
# 从现有计数开始
existing_data = {'apple': 5, 'banana': 3}
# 从现有字典创建 defaultdict
fruit_counts = defaultdict(int, existing_data)
# 追加更多计数
fruit_counts['apple'] += 2 # 5 + 2 = 7
fruit_counts['orange'] += 1 # 0 + 1 = 1 (new key, starts at 0)
print(dict(fruit_counts))
# Output: {'apple': 7, 'banana': 3, 'orange': 1}自定义默认工厂
你可以提供任何可调用对象作为默认工厂:
from collections import defaultdict
# 使用 lambda 自定义默认值
page_views = defaultdict(lambda: {'views': 0, 'unique': 0})
page_views['home']['views'] = 100
page_views['home']['unique'] = 75
print(page_views['home'])
# Output: {'views': 100, 'unique': 75}
print(page_views['about']) # New key gets default dictionary
# Output: {'views': 0, 'unique': 0}重要说明
访问 vs. 检查键:
from collections import defaultdict
counts = defaultdict(int)
# 访问缺失键会创建它
value = counts['missing'] # Creates 'missing' with value 0
print('missing' in counts) # Output: True
# 若想在不创建键的情况下检查,使用 'in' 或 .get()
counts2 = defaultdict(int)
print('missing' in counts2) # Output: False (doesn't create key)
print(counts2.get('missing')) # Output: None (doesn't create key)39.5) (可选)有用的迭代工具
itertools 模块提供用于创建高效迭代器的函数。这些工具帮助你以强大的方式处理序列,而无需创建很大的中间列表。
39.5.1) 使用 chain() 串联可迭代对象
chain() 函数将多个可迭代对象组合为一个迭代器,按顺序从每个可迭代对象中产出元素。
chain() 接受的内容:
- 多个可迭代对象(列表、元组、字符串等),作为单独参数传入
chain() 返回的内容:
- 一个迭代器:先产出第一个可迭代对象的所有元素,再产出第二个的所有元素,依此类推
关键优势:
- 比用
+拼接更省内存(不会创建中间列表) - 适用于任何可迭代对象,不仅是列表
from itertools import chain
# 合并多个列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
combined = chain(list1, list2, list3)
print(list(combined)) # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]这比用 + 拼接列表更省内存,尤其是在处理大型序列时:
from itertools import chain
# 在不创建大型合并列表的情况下处理多个数据源
students_class_a = ["Alice", "Bob", "Charlie"]
students_class_b = ["Diana", "Eve", "Frank"]
students_class_c = ["Grace", "Henry", "Iris"]
# 在不创建合并列表的情况下遍历所有学生
for student in chain(students_class_a, students_class_b, students_class_c):
print(f"Processing: {student}")
# Output:
# Processing: Alice
# Processing: Bob
# Processing: Charlie
# Processing: Diana
# Processing: Eve
# Processing: Frank
# Processing: Grace
# Processing: Henry
# Processing: Iris你可以串联不同类型的可迭代对象:
from itertools import chain
# 串联列表、元组与字符串
numbers = [1, 2, 3]
letters = ("a", "b", "c")
word = "xyz"
combined = chain(numbers, letters, word)
print(list(combined)) # Output: [1, 2, 3, 'a', 'b', 'c', 'x', 'y', 'z']39.5.2) 使用 cycle() 重复元素
cycle() 函数创建一个无限迭代器,会反复循环产出某个可迭代对象的元素。
cycle() 接受的内容:
- 一个可迭代对象(列表、元组、字符串等)
cycle() 返回的内容:
- 一个无限迭代器,会重复产出可迭代对象中的元素
- 当到达末尾后,会从头开始
关键特性:
- 创建一个无限(infinite)迭代器——不会自行停止
- 必须配合停止条件(计数器、
break或zip()) - 省内存:不会创建数据副本
from itertools import cycle
# 创建一个无限循环的颜色序列
colors = cycle(["red", "green", "blue"])
# 取前 10 个颜色
for i, color in enumerate(colors):
if i >= 10:
break
print(f"Item {i}: {color}")
# Output:
# Item 0: red
# Item 1: green
# Item 2: blue
# Item 3: red
# Item 4: green
# Item 5: blue
# Item 6: red
# Item 7: green
# Item 8: blue
# Item 9: red警告:cycle() 会创建一个无限迭代器。务必配合停止条件(比如计数器或 break 语句)使用,否则你会创建一个无限循环。
一个实用用例是在不同值之间交替:
from itertools import cycle
# 在表格行之间交替使用两种背景色
row_colors = cycle(["white", "lightgray"])
rows = ["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"]
for row, color in zip(rows, row_colors):
print(f"{row}: background-color: {color}")
# Output:
# Row 1: background-color: white
# Row 2: background-color: lightgray
# Row 3: background-color: white
# Row 4: background-color: lightgray
# Row 5: background-color: white这里我们使用 zip()(我们在第 37 章学过)把每一行与一种颜色配对。cycle() 迭代器会根据需要自动重复颜色。
39.5.3) 组合 chain() 与 cycle()
你可以组合 itertools 函数来形成更复杂的模式:
from itertools import chain, cycle
# 创建一个在多个序列之间循环的模式
pattern1 = [1, 2, 3]
pattern2 = [10, 20]
# 先串联这些模式,然后对结果进行循环
combined_pattern = cycle(chain(pattern1, pattern2))
# 取前 12 个值
for i, value in enumerate(combined_pattern):
if i >= 12:
break
print(value, end=" ")
# Output: 1 2 3 10 20 1 2 3 10 20 1 2
print() # Newline这会创建一个重复模式:1、2、3、10、20、1、2、3、10、20、...
下面是一个创建轮换排班表的实用示例:
from itertools import cycle
# 为团队成员创建轮换排班
team_members = ["Alice", "Bob", "Charlie"]
schedule = cycle(team_members)
# 轮换给团队成员分配任务
tasks = [
"Review code",
"Write tests",
"Update documentation",
"Fix bug #123",
"Implement feature X",
"Deploy to staging"
]
print("Task Assignments:")
for task, assignee in zip(tasks, schedule):
print(f" {assignee}: {task}")
# Output:
# Task Assignments:
# Alice: Review code
# Bob: Write tests
# Charlie: Update documentation
# Alice: Fix bug #123
# Bob: Implement feature X
# Charlie: Deploy to staging在本章中,我们探索了五个扩展 Python 能力的必备标准库模块:
random:生成随机数、进行随机选择并打乱序列——对仿真、游戏与测试至关重要datetime:处理日期、时间与持续量——计算年龄、安排事件并格式化时间戳json:使用通用的 JSON 格式与其他程序交换数据——保存应用状态、处理 Web API 并存储配置collections:使用专用容器,例如用于计数的Counter与用于自动创建键的defaultdictitertools:用chain()合并序列、用cycle()重复模式,从而创建高效迭代器
这些模块都是 Python 标准库的一部分——它们始终可用、经过充分测试,并能优雅地解决常见编程问题。随着你构建更复杂的程序,你会发现自己会频繁地使用这些工具。它们体现了 Python “自带电池(batteries included)”的理念——为日常编程任务提供强大且开箱即用的解决方案。