模块和标准库

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)。对于标准库模块,优先使用完整名称;它会让代码更可读。
import math 引入整个模块,然后你可以用点号来访问内容:math.sqrt(16)。from math import sqrt 获取一个名称,这样你可以去掉前缀。as 关键字可以重命名一个很长的导入,但对于标准库来说,完整名称读起来更清楚。 random
random 模块生成随机数并做出随机选择。在游戏、模拟、随机采样以及任何需要不可预测性的地方使用它。设置一个种子会使结果可重复:相同的种子每次都产生相同的序列。
import random
random.random() # 0 到 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,总是相同的值相同的种子在任何机器上每次都产生相同的序列。
random.choice() 从列表中挑选一项,random.randint(1, 10) 给出范围内的整数(包含端点)。想要每次运行都得到相同的结果,比如对于测试?先调用 random.seed(),序列就锁定了。 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) # Truemath 涵盖了超越 `+ - * /` 的所有东西:平方根、幂、对数、三角函数,还有 math.pi 之类的常数。只要计算需要超越基本运算符的东西,就直接使用它,省得自己写公式。 datetime
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 | 小时(24h) | 09 |
%M | 分钟 | 42 |
%B | 完整月份名称 | January |
datetime.now() 给你当前的日期和时间。strftime() 用 %Y-%m-%d 之类的代码把它转成字符串,strptime() 反过来,把字符串解析回来。timedelta 是一个时长,你可以加或减,所以 today + timedelta(days=1) 就是明天。 os 和 pathlib
pathlib 是处理文件路径的现代方式。Path 对象让你可以用 / 运算符来构建、检查和导航路径。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。
pathlib.Path 把文件路径当作对象来处理:.exists()、.read_text()、.write_text()、.glob()。`/` 运算符连接片段,所以 p / "report.csv" 读起来像一个真实路径。在新代码中优先使用 pathlib 而不是较老的 os.path。 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 是重复多少次。更多的重复给出更稳定的测量。
timeit.timeit() 通过一遍遍运行代码来计时,然后你除以运行次数来得到稳定的每次调用数。这是你如何确定"这两个中哪个更快"而不是猜测。一次运行太吵闹,不可信,所以让它循环。 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))string 模块给你现成的字符集:string.ascii_letters、string.digits、string.punctuation。它们是纯字符串,所以你可以循环或从中采样。无论何时你需要一个已知的字母表来构建或检查,都很方便。 创建你自己的模块
任何 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 文件的目录。
import utils 然后 utils.clamp(...),或用 from utils import clamp 拉一个名称。这就是你如何跨文件分割一个增长的程序的方式。 __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这是任何既可用作独立脚本的模块的标准模式。
if __name__ == "__main__": 让一个文件既是可导入的模块,又是可运行的脚本。保护下的代码在你做 python utils.py 时运行,当另一个文件做 import utils 时被跳过。方便在模块底部塞一个快速演示或测试。 标准库亮点
一些其他值得了解的模块。每个都解决一个常见问题,自己实现需要大量工作。
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)}")
