模块与标准库
Python 自带了大量可以直接使用的工具:随机数、数学、日期、文件路径等等。这些工具存在于模块中,你可以用 import 把它们引入代码。在上一章里,你已经用过 import json。本章将完整介绍导入机制,并带你了解标准库中最有用的部分。
导入模块
最简单的导入方式是引入整个模块,然后用点号访问它的内容。你也可以从模块中导入特定的名字,直接使用它们而不需要前缀。别名可以缩短较长的名字。
import math
math.sqrt(16) # 4.0
math.pi # 3.141592653589793
math.floor(3.9) # 3
math.ceil(3.1) # 4从模块中导入特定的名字,这样你可以直接使用它们:
from math import sqrt, pi
sqrt(16) # 4.0(不需要 "math." 前缀)
pi # 3.141592653589793为模块或名字起一个别名以缩短它:
import math as m
m.sqrt(16) # 4.0
from math import sqrt as square_root
square_root(25) # 5.0别名在流行的第三方库中很常见(import numpy as np、import pandas as pd)。对于标准库模块,优先使用完整名字;这样代码更容易阅读。
random
random 模块用于生成随机数和做随机选择。它可用于游戏、模拟、随机抽样,以及任何需要不可预测性的场景。设置种子可以让结果可复现:同样的种子每次都会产生同样的序列。
import random
random.random() # 0 到 1 之间的浮点数(不含 1)
random.randint(1, 10) # 1 到 10 的整数(两端都包含)
random.uniform(1.0, 10.0) # 1.0 到 10.0 之间的浮点数
colours = ["red", "green", "blue"]
random.choice(colours) # 选一个元素
random.choices(colours, k=3) # 选 k 个元素(有放回)
random.sample(colours, k=2) # 选 k 个元素(无放回)
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers) # 就地打乱,返回 None为了让结果可复现(在测试和数据科学中很有用),在生成之前设置种子:
random.seed(42)
random.randint(1, 100) # 种子为 42 时,总是返回相同的值同样的种子在任何机器上每次都会产生同样的序列。
math
math 模块在基本算术运算符之外提供了更高级的数学运算。平方根、幂、对数、三角函数,以及 pi 和无穷大等特殊值,都在这里。
import math
math.sqrt(25) # 5.0
math.pow(2, 10) # 1024.0(等同于 2 ** 10,但总是返回 float)
math.log(100, 10) # 2.0(以 10 为底的对数)
math.log(math.e) # 1.0(自然对数)
math.sin(math.pi / 2) # 1.0
math.cos(0) # 1.0
math.ceil(3.2) # 4
math.floor(3.9) # 3
math.trunc(3.9) # 3(对正数与 int() 等同)
math.inf # 无穷大
math.isnan(float("nan")) # True
math.isinf(math.inf) # Truedatetime
datetime 模块用于处理日期和时间。datetime.now() 给你当前的日期和时间。strftime() 将其格式化为字符串。strptime() 把字符串解析成 datetime。timedelta 表示一段时长,你可以做加减运算。
from datetime import datetime, date, timedelta
now = datetime.now() # 当前日期和时间
today = date.today() # 仅当前日期
print(now.year, now.month, now.day)
print(now.hour, now.minute, now.second)
# 格式化
print(now.strftime("%Y-%m-%d")) # "2024-01-15"
print(now.strftime("%d %B %Y, %H:%M")) # "15 January 2024, 09:42"
# 解析
deadline = datetime.strptime("2024-12-31", "%Y-%m-%d")
# 算术运算
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)
diff = deadline - now
print(f"{diff.days} days until deadline")常用的 strftime 代码:
| 代码 | 含义 | 示例 |
|---|---|---|
%Y | 4 位年份 | 2024 |
%m | 月份(补零) | 01 |
%d | 日(补零) | 15 |
%H | 小时(24 小时制) | 09 |
%M | 分钟 | 42 |
%B | 完整月份名 | January |
os 和 pathlib
pathlib 是处理文件路径的现代方式。Path 对象让你可以使用 / 运算符来构建、检查和导航路径。os 提供对环境变量和较低层 OS 操作的访问。新代码建议优先使用 pathlib。
from pathlib import Path
p = Path("data/reports")
p.exists() # 路径存在则为 True
p.is_dir() # 是目录则为 True
p.is_file() # 是文件则为 True
p.mkdir(parents=True, exist_ok=True) # 创建目录
for f in p.glob("*.csv"): # 目录中所有 CSV 文件
print(f.name) # 只显示文件名
report = p / "report_jan.csv" # / 运算符用于拼接路径
report.stem # "report_jan"(不含扩展名的名称)
report.suffix # ".csv"
report.parent # Path("data/reports")
content = report.read_text() # 直接读取文件内容
report.write_text("new content\n") # 直接写入对于 os 模块:
import os
os.getcwd() # 当前工作目录
os.listdir(".") # 列出目录内容
os.path.exists("data.txt") # 路径存在则为 True
os.path.join("data", "file.txt") # "data/file.txt"(跨平台)
os.environ.get("HOME") # 读取一个环境变量新代码优先使用 pathlib。当你需要环境变量或处理只接受字符串的老式 API 时,使用 os。
timeit
timeit 测量代码运行所需的时间。当你想比较两种实现并选择更快的那一种时,它很有用。多次运行代码以获得稳定的测量值。
import timeit
# 给单条语句计时
timeit.timeit("sum(range(1000))", number=10000)
# 给更复杂的代码块计时
setup = "data = list(range(1000))"
code = "[x * 2 for x in data]"
time = timeit.timeit(code, setup=setup, number=10000)
print(f"{time:.4f} seconds for 10,000 runs")number 是重复次数。重复次数越多,测量结果越稳定。
string
string 模块提供了预先定义好的字符串常量,包括字母、数字和标点符号。当你需要检查字符或从特定字母表生成随机字符串时很有用。
import string
string.ascii_lowercase # "abcdefghijklmnopqrstuvwxyz"
string.ascii_uppercase # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
string.ascii_letters # 两者合并
string.digits # "0123456789"
string.punctuation # 所有标点符号当你需要检查字符或生成随机字符串时很有用:
import string, random
chars = string.ascii_letters + string.digits
password = "".join(random.choices(chars, k=12))创建自己的模块
任何 Python 文件都是一个模块。要在另一个文件中使用它,按文件名(去掉 .py)导入即可。你可以导入整个模块,通过点号访问其内容;也可以直接导入特定的名字。
# utils.py
def clamp(value, lo, hi):
return max(lo, min(value, hi))
PI = 3.14159# main.py
import utils
utils.clamp(150, 0, 100) # 100
utils.PI # 3.14159
from utils import clamp
clamp(50, 0, 100) # 50Python 会在导入文件所在的同一目录(以及其他一些位置)查找模块。对于较大的项目,模块会被组织成包:包含 __init__.py 文件的目录。
__name__ == "__main__"
当 Python 直接运行一个文件时,__name__ 被设为 "__main__"。当同一文件作为模块被导入时,__name__ 是模块名。这种写法让你可以编写直接执行文件时运行的代码,而当该文件被其他模块导入时这些代码会被跳过。
# utils.py
def clamp(value, lo, hi):
return max(lo, min(value, hi))
if __name__ == "__main__":
# 这只在你执行 python utils.py 时运行
# 当执行 import utils 时不会运行
print(clamp(150, 0, 100)) # 100这是任何同时也可以作为独立脚本运行的模块的标准写法。
标准库精华
这里再介绍几个值得了解的模块。每一个都解决了一个常见问题,如果自己实现需要花费大量工作。
collections:特殊的容器类型:
from collections import Counter, defaultdict, deque
Counter(["a", "b", "a", "c", "a"]) # Counter({'a': 3, 'b': 1, 'c': 1})
defaultdict(list) # 自动为缺失键创建值的字典
deque([1, 2, 3], maxlen=5) # 两端都能快速追加/弹出itertools:处理可迭代对象的工具:
import itertools
list(itertools.chain([1, 2], [3, 4])) # [1, 2, 3, 4]
list(itertools.islice(range(100), 5)) # [0, 1, 2, 3, 4]
list(itertools.combinations([1, 2, 3], 2)) # [(1, 2), (1, 3), (2, 3)]
list(itertools.product([0, 1], repeat=2)) # [(0,0), (0,1), (1,0), (1,1)]sys:访问 Python 解释器:
import sys
sys.argv # 命令行参数列表
sys.exit(1) # 以状态码退出
sys.version # Python 版本字符串第三方包:除了标准库,pip 可以安装社区包:
pip install requests # HTTP 库
pip install pandas # 数据处理
pip install numpy # 数值计算第三方包不在本指南的讨论范围内,但模式总是一样的:pip install,然后 import。
实战示例
结合 random、string 和 datetime 生成带时间戳的唯一游戏 ID:
import random
import string
from datetime import datetime
def generate_game_id(length: int = 8) -> str:
chars = string.ascii_uppercase + string.digits
return "".join(random.choices(chars, k=length))
def timestamp() -> str:
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
game_id = generate_game_id()
print(f"[{timestamp()}] Starting game {game_id}")
scores = [random.randint(50, 100) for _ in range(5)]
print(f"Round scores: {scores}")
print(f"Best: {max(scores)}")
