Python 历史与版本演进
Python 从 1989 年圣诞节的一次「业余项目」起步,历经三十多年演进,成为当今最主流的编程语言之一。理解版本演进的历史,不仅能帮你读懂老代码,更能让你理解每一个现代特性背后的设计动机。本节将梳理关键时间线、版本特性与生命周期策略。
前置知识
阅读本节前,建议先了解:Python 是什么,理解 Python 的基本定位与「Python 之禅」。
基础概念
Python 的版本号遵循 主版本.次版本.修订号(如 3.12.5)的语义化格式。主版本变更(2 → 3)意味着不向后兼容的重大改动;次版本(3.11 → 3.12)每年发布一次,带来新语法与性能改进;修订号(3.12.4 → 3.12.5)只包含 Bug 修复与安全补丁。
| 概念 | 英文 | 说明 |
|---|---|---|
| 主版本 | Major Version | 破坏性变更,如 Python 2 到 3 |
| 次版本 | Minor Version | 年度功能发布,如 3.12 |
| 修订号 | Micro / Patch | Bug 与安全修复 |
| 预发布版 | Pre-release | alpha、beta、rc 等测试版本 |
| 生命周期 | Lifecycle | 从功能冻结到停止支持的全过程 |
| 增强提案 | PEP | Python Enhancement Proposal,特性设计的正式文档 |
语法与用法
在写任何代码之前,先确认运行环境的版本,是养成良好习惯的第一步:
# 查看当前 Python 版本信息
import sys
# version_info 是具名元组,可直接比较
print(sys.version_info)
print(f"主版本:{sys.version_info.major}")
print(f"次版本:{sys.version_info.minor}")
print(f"修订号:{sys.version_info.micro}")
print(f"发行级别:{sys.version_info.releaselevel}")
print(sys.version) # 含编译信息的完整字符串sys.version_info(major=3, minor=12, micro=5, releaselevel='final', serial=0)
主版本:3
次版本:12
修订号:5
发行级别:final
3.12.5 (main, Aug 6 2024, 19:08:49) [Clang 15.0.0]详细说明
版本时间线
下表列出了 Python 3 各主要版本的关键特性,是理解现代 Python 的必备参照:
| 版本 | 发布时间 | 关键特性 | 状态 |
|---|---|---|---|
| Python 1.0 | 1994-01 | 函数式工具 lambda、map、filter | 已停止 |
| Python 2.0 | 2000-10 | 列表推导、垃圾回收改进、Unicode | 2020-01 停止支持 |
| Python 3.0 | 2008-12 | print 变函数、默认 Unicode、统一整除 | 已停止 |
| Python 3.6 | 2016-12 | f-string、变量注解、字典有序 | 已停止 |
| Python 3.7 | 2018-06 | dataclasses、async/await 稳定、__future__ 延迟注解 | 2023-06 停止支持 |
| Python 3.8 | 2019-10 | 海象运算符 :=、仅位置参数 /、f-string = | 2024-10 停止支持 |
| Python 3.9 | 2020-10 | 内置泛型 list[int]、字典合并 ` | 、str.removeprefix` |
| Python 3.10 | 2021-10 | 结构化模式匹配 match、联合类型 X | Y、更清晰的错误 | 安全支持中 |
| Python 3.11 | 2022-10 | 异常组 except*、Self 类型、大幅性能提升(快约 25%) | 安全支持中 |
| Python 3.12 | 2023-10 | 类型参数语法 type X = ...、f-string 增强、Perf 优化 | 全面支持中 |
| Python 3.13 | 2024-10 | 实验性 free-threaded(无 GIL)、改进的 REPL、JIT 实验特性 | 全面支持中 |
| Python 3.14 | 2025-10 | 延迟注解求值、模板字符串 t-string、多解释器增强 | 全面支持中 |
版本节奏
自 Python 3.x 的 3.6 起,CPython 形成了「每年 10 月发布一个次版本」的固定节奏。每个次版本获得约 5 年支持:前 2 年全面支持(含新特性),后 3 年仅提供安全修复。
Python 2 与 Python 3 的分裂
Python 3.0 为了修复历史遗留设计缺陷,进行了不向后兼容的重构,这成为编程语言史上最著名的迁移案例之一:
| 变化点 | Python 2 | Python 3 |
|---|---|---|
| 输出 | print "hello" | print("hello") |
| 字符串 | str 为字节,unicode 为文本 | str 为文本,bytes 为字节 |
| 整除 | 3 / 2 == 1(整数除法) | 3 / 2 == 1.5(真除法) |
| 迭代器 | dict.keys() 返回列表 | 返回视图对象 |
| 输入 | raw_input() | input() |
| 异常语法 | except E, e: | except E as e: |
# Python 3 中以上差异的正确写法
text: str = "中文文本" # str 表示 Unicode 文本
data: bytes = text.encode("utf-8") # bytes 表示字节序列
print(text)
print(data)
print(text.encode("utf-8").decode("utf-8"))
print(f"整数真除法:3 / 2 = {3 / 2}") # 1.5
print(f"整数地板除:3 // 2 = {3 // 2}") # 1中文文本
b'\xe4\xb8\xad\xe6\x96\x87\xe6\x96\x87\xe6\x9c\xac'
中文文本
整数真除法:3 / 2 = 1.5
整数地板除:3 // 2 = 1Python 2 已于 2020 年 1 月 1 日正式停止支持,所有新项目都应使用 Python 3。
近代版本的重要特性
Python 3.8:海象运算符
:= 允许在表达式内部赋值,避免了「计算一次、再用一次」的重复调用:
# Python 3.8+
# 不推荐:调用两次 len(),或先单独赋值
data = [1, 2, 3, 4, 5]
if len(data) > 3:
print(f"列表长度 {len(data)} 超过限制")
# 推荐:海象运算符一次赋值并在条件中使用
if (n := len(data)) > 3:
print(f"列表长度 {n} 超过限制")列表长度 5 超过限制
列表长度 5 超过限制Python 3.9:内置泛型与字典合并
# Python 3.9+:内置类型可直接作为泛型,无需 typing.List
def merge_scores(a: dict[str, int], b: dict[str, int]) -> dict[str, int]:
"""合并两份成绩单,后者覆盖前者"""
return a | b
print(merge_scores({"alice": 90}, {"bob": 85, "alice": 95}))
# 字符串新方法
filename = "report.tar.gz"
print(filename.removeprefix("report.")) # 去掉前缀
print(filename.removesuffix(".gz")) # 去掉后缀{'alice': 95, 'bob': 85}
tar.gz
report.tarPython 3.10:结构化模式匹配
match 语句提供了比 if/elif 更清晰的多分支结构匹配:
# Python 3.10+
def handle_command(command: dict[str, object]) -> str:
"""根据命令字典分发处理逻辑"""
match command:
case {"action": "create", "name": str(name)}:
return f"创建资源:{name}"
case {"action": "delete", "name": str(name)}:
return f"删除资源:{name}"
case {"action": "list", "limit": int(limit)}:
return f"列出最多 {limit} 项"
case {"action": "list"}:
return "列出全部(默认限制)"
case _:
return "未知命令"
print(handle_command({"action": "create", "name": "user"}))
print(handle_command({"action": "list", "limit": 10}))
print(handle_command({"action": "list"}))
print(handle_command({"action": "unknown"}))创建资源:user
列出最多 10 项
列出全部(默认限制)
未知命令Python 3.11:异常组与性能提升
# Python 3.11+:异常组可一次抛出多个独立异常
def validate_user(user: dict[str, object]) -> None:
"""校验用户数据,收集所有错误后一次性抛出"""
errors: list[ValueError] = []
if not user.get("name"):
errors.append(ValueError("name 不能为空"))
if user.get("age", 0) < 18:
errors.append(ValueError("age 必须大于等于 18"))
if errors:
raise ExceptionGroup("用户数据校验失败", errors)
try:
validate_user({"name": "", "age": 15})
except* ValueError as group:
# except* 可捕获异常组中匹配类型的子集
for exc in group.exceptions:
print(f"发现错误:{exc}")发现错误:name 不能为空
发现错误:age 必须大于等于 18Python 3.12:类型参数语法
PEP 695 让泛型写法更简洁自然:
# Python 3.12+
type Priority = int # 类型别名
type Callback[T] = list[str] | None # 泛型类型别名
class Stack[T]:
"""一个通用的后进先出栈"""
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def __len__(self) -> int:
return len(self._items)
stack: Stack[str] = Stack()
stack.push("a")
stack.push("b")
print(len(stack), stack.pop(), stack.pop())2 b aPython 3.13:自由线程与改进 REPL
# Python 3.13+:新的交互式 REPL 支持多行编辑与彩色回溯(仅终端体验)
# 检查是否运行在 free-threaded(无 GIL)构建上
import sys
# 该属性仅在支持 free-threading 的构建中存在
free_threaded = getattr(sys, "_is_gil_enabled", lambda: None)()
print(f"GIL 是否启用:{free_threaded}")GIL 是否启用:Truefree-threaded 仍在实验阶段
Python 3.13 的 free-threaded(无 GIL)构建需要在安装时专门选择,且部分 C 扩展尚不兼容。生产环境建议继续使用标准构建,等待生态完善。
版本生命周期与支持策略
CPython 采用「滚动支持窗口」:每个次版本约 5 年,其中前 2 年为全面支持(Bug 修复),后 3 年为安全支持(仅安全补丁):
| 阶段 | 时长 | 内容 |
|---|---|---|
| 特性开发 | 约 1 年 | 新特性合并,进入 alpha/beta |
| Bug 修复(Bugfix) | 约 2 年 | 修复 Bug,接受安全补丁 |
| 安全修复(Security) | 约 3 年 | 只修复安全漏洞 |
| 生命周期终止(EOL) | — | 不再发布任何更新 |
# 一个实用的版本守卫:在程序入口检查版本是否满足要求
import sys
from dataclasses import dataclass
@dataclass(frozen=True)
class VersionRequirement:
"""版本要求描述"""
minimum: tuple[int, int]
reason: str
def check(self) -> bool:
"""检查当前解释器是否满足要求"""
return sys.version_info[:2] >= self.minimum
def require(req: VersionRequirement) -> None:
"""不满足版本要求时直接退出并给出清晰提示"""
if not req.check():
current = ".".join(map(str, sys.version_info[:3]))
required = ".".join(map(str, req.minimum))
raise SystemExit(
f"需要 Python {required}+({req.reason}),当前版本 {current}"
)
require(VersionRequirement((3, 11), "使用了 ExceptionGroup 与会话优化"))
print("版本检查通过")版本检查通过实战示例
示例一:编写兼容多版本的代码
现实中要在一段时间内支持多个 Python 版本,可用 sys.version_info 分支或特性探测:
import sys
# 方案一:按版本号分支
if sys.version_info >= (3, 12):
type NumberList = list[int] # 3.12+ 的 type 语句
print("使用 3.12+ 的类型参数语法")
else:
from typing import List
NumberList = List[int] # 旧版本回退写法
print("使用兼容写法")
# 方案二:特性探测(推荐,更健壮)
import shutil
# shutil.rmtree 的 onexc 参数是 3.12 新增的
supports_onexc = "onexc" in shutil.rmtree.__doc__ if shutil.rmtree.__doc__ else False
print(f"支持 onexc 参数:{supports_onexc}")使用 3.12+ 的类型参数语法
支持 onexc 参数:True示例二:用特性开关渐进式采用新语法
大型项目常用「渐进升级」策略:先升级解释器,再逐步采用新特性:
# 一份通用的「版本能力检测表」,便于项目内部查询
import sys
import typing
CAPABILITIES: dict[str, tuple[int, int]] = {
"walrus": (3, 8), # 海象运算符
"dict_union": (3, 9), # 字典合并运算符 |
"structural_match": (3, 10), # 结构化模式匹配
"exception_group": (3, 11), # 异常组
"type_params": (3, 12), # type 语句与泛型语法
"free_threaded": (3, 13), # 自由线程构建
}
def supported(feature: str) -> bool:
"""检查当前解释器是否支持指定特性"""
required = CAPABILITIES[feature]
return sys.version_info[:2] >= required
for name in CAPABILITIES:
mark = "支持" if supported(name) else "不支持"
print(f"{name:<18} {mark}")walrus 支持
dict_union 支持
structural_match 支持
exception_group 支持
type_params 支持
free_threaded 不支持示例三:识别并替换过时写法
下表列出了常见的老式写法与其现代替代:
| 过时写法 | 现代写法 | 引入版本 |
|---|---|---|
typing.List[int] | list[int] | 3.9 |
typing.Dict[str, int] | dict[str, int] | 3.9 |
typing.Optional[int] | int | None | 3.10 |
typing.Union[int, str] | int | str | 3.10 |
"{}".format(x) | f"{x}" | 3.6 |
显式泛型 TypeVar | class C[T]: | 3.12 |
os.path.join 拼接 | pathlib.Path / "x" | 3.6 起流行 |
# 现代写法示例(Python 3.12+)
from pathlib import Path
type Id = int
type UserMap = dict[Id, str]
def find_user(users: UserMap, user_id: Id) -> str | None:
"""按 ID 查找用户,找不到返回 None"""
return users.get(user_id)
def file_size(path: str) -> int:
"""返回文件字节大小,路径用 pathlib 统一处理"""
target = Path(path) / "report.txt"
return target.stat().st_size if target.exists() else 0
print(find_user({1: "alice", 2: "bob"}, 2))
print(find_user({1: "alice"}, 99))
print(file_size("."))bob
None
0注意事项
- 不要使用已 EOL 的版本:Python 3.8 及更早版本已停止安全支持,存在未修复漏洞风险。
- 注意版本引入时机:
match需 3.10+,type语句需 3.12+,写库时要考虑用户环境。 - 预发布版不适合生产:alpha/beta/rc 版本 API 可能变动,仅在测试环境体验。
- free-threaded 是实验特性:需要专门安装,且部分 C 扩展仍不兼容。
sys.version_info比较用元组:sys.version_info[:2] >= (3, 11),不要用字符串比较。- Python 2 代码不要直接运行:
print "x"在 Python 3 中是语法错误,需先做迁移。
最佳实践
- 新项目统一使用 Python 3.12+:享受类型参数、性能优化与现代错误提示。
- 用
pyproject.toml声明最低版本:通过requires-python = ">=3.11"明确支持范围。 - 在 CI 中测试多个版本矩阵:确保声明支持的版本都真实可用。
- 关注官方 What's New 文档:每次升级前阅读对应版本的新特性与不兼容变更。
- 用工具自动升级语法:pyupgrade、ruff 的 UP 规则可批量替换过时写法。
- 为依赖设定安全更新策略:定期检查依赖的 Python 版本支持情况,避免被 EOL 拖累。
# 用 pyupgrade 把老式 typing 写法批量升级到现代语法
pip install pyupgrade
pyupgrade --py312-plus src/**/*.py
# 用 ruff 检查并自动修复过时语法
ruff check --select UP --fix src/下一节
继续学习:CPython 解释器原理