Python(二十一):第二十章:Python 语法新特性总结

第二十章:Python 语法新特性总结 (3.8 ~ 3.13)

Python 语言在不断发展,每个新版本都会带来一些有用的新特性、性能改进和语法糖,使得编写代码更加高效和愉悦。本章将简要回顾从 Python 3.8 到 3.13 版本引入的一些值得关注的语法和核心库特性。

Python 3.8 主要特性

Python 3.8 带来了赋值表达式(海象操作符)、仅位置参数、f-string 调试支持等重要更新。

1. 赋值表达式 (海象操作符) :=

海象操作符 := 允许你在表达式内部为变量赋值。这可以在某些情况下简化代码,尤其是在 if 语句或 while 循环中,当你需要先计算一个值,然后判断这个值,并且后续还想使用这个值时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# === 海象操作符演示 ===
from print_utils import * # 假设 print_utils.py 可用
from typing import List, Any


def demo_walrus_operator(data: List[Any]) -> None:
"""演示海象操作符 (:=) 的使用。"""
print_subheader("1. 海象操作符 (Assignment Expressions) `:=`")

# 示例1: 在 if 条件中赋值并使用
if (n := len(data)) > 3:
print_success(f"列表长度为 {n},大于3。前三个元素: {data[:3]}")
else:
print_info(f"列表长度为 {n},不大于3。")


if __name__ == '__main__':
print_header("Python 3.8 特性演示")
sample_data_list: List[Any] = [1, "abc", [1, 2], 3]
demo_walrus_operator(sample_data_list)

2. 仅位置参数 (/)

函数定义中可以使用 / 来指明其前面的参数只能通过位置传递,不能作为关键字参数传递。这有助于库作者设计更清晰、更不易出错的 API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# === 仅位置参数演示 ===
from print_utils import *

def demo_positional_only_args() -> None:
"""演示仅位置参数的使用。"""
print_subheader("2. 仅位置参数 (`/`)")

def create_user(user_id: int, name: str, /, age: int = 30, city: str = "Unknown") -> dict:
"""
创建用户信息字典。
user_id 和 name 只能通过位置传递。
age 和 city 可以是位置参数或关键字参数。
"""
return {"id": user_id, "name": name, "age": age, "city": city}

print_success(str(create_user(1, "Alice"))) # 正确:位置参数
print_success(str(create_user(1, "Alice", age=25))) # 正确:位置参数和关键字参数
print_success(str(create_user(1, "Alice", 25))) # 正确:age 作为位置参数也是可以的

# 演示真正的错误情况
try:
create_user(id=1, name="Alice") # 错误:/ 之前的参数不能用关键字方式传递
except TypeError as e:
print_error(f"在尝试执行操作时发生错误: {e}")



if __name__ == '__main__':
# 此处不重复 print_header,假设按特性运行
demo_positional_only_args()

3. f-string 增强 (=) 用于调试

f-string 现在支持 = 说明符,可以在输出中包含表达式文本及其计算结果,非常便于调试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# === f-string 调试增强演示 ===
from print_utils import *

def demo_fstring_debug() -> None:
"""演示 f-string 中 `=` 的调试功能。"""
print_subheader("3. f-string 增强 (`=`) 用于调试")

app_name: str = "MyApplication"
app_version: float = 2.1
user_count: int = 1050

# 使用 = 直接打印变量名和值
print_info(f"调试信息: {app_name=}, {app_version=}, {user_count=}")
# 输出示例: 调试信息: app_name='MyApplication', app_version=2.1, user_count=1050

# 也可以用于更复杂的表达式
result: int = user_count * 2
print_info(f"计算结果: {user_count * 2 = }") # 注意 `=` 在表达式之后
# 输出示例: 计算结果: user_count * 2 = 2100

if __name__ == '__main__':
demo_fstring_debug()

Python 3.9 主要特性

Python 3.9 引入了新的字典合并操作符,类型注解可以使用内置集合类型,并为字符串添加了移除前缀/后缀的方法。

1. 字典合并与更新操作符 (||=)

新的操作符 | 用于合并两个字典,|= 用于原地更新字典。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# === 字典合并与更新操作符演示 ===
from print_utils import *
from typing import Dict, Any # 兼容旧版类型提示,但在3.9+中 dict 即可


def demo_dict_merge_operators() -> None:
"""演示字典的 | 和 |= 操作符。"""
print_subheader("1. 字典合并 (`|`) 与更新 (`|=`) 操作符")

dict_a: Dict[str, int] = {"name_id": 1, "value": 10}
dict_b: Dict[str, int] = {"value": 20, "status": 0} # "value"键与dict_a重复

# 合并操作 (|),如果键重复,则右边字典的值优先
merged_dict: Dict[str, int] = dict_a | dict_b
print_info(f"dict_a: {dict_a}")
print_info(f"dict_b: {dict_b}")
print_success(f"合并后 (merged_dict = dict_a | dict_b): {merged_dict}")
# 输出: {'name_id': 1, 'value': 20, 'status': 0}

# 原地更新操作 (|=)
dict_c: Dict[str, int] = {"item_code": 101, "quantity": 5}
dict_d: Dict[str, Any] = {"quantity": 15, "description": "Gadget"}
print_info(f"\n原始 dict_c: {dict_c}")
dict_c |= dict_d # dict_c 被修改
print_success(f"原地更新后 (dict_c |= dict_d): {dict_c}")
# 输出: {'item_code': 101, 'quantity': 15, 'description': 'Gadget'}


if __name__ == '__main__':
print_header("Python 3.9 特性演示")
demo_dict_merge_operators()

2. 类型注解的内置集合类型 (泛型)

现在可以直接使用内置的集合类型 (如 list, dict, tuple, set) 作为泛型类型进行注解,而无需从 typing 模块导入大写版本 (如 List, Dict)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# === 内置集合类型注解演示 ===
from print_utils import *
# from typing import List, Dict # 在 Python 3.9+ 中不再必需,但为了兼容性或清晰性仍可使用

def demo_builtin_generic_types(names: list[str], scores: dict[str, int]) -> tuple[int, float]:
"""
演示使用内置集合类型进行类型注解。
参数:
names (list[str]): 字符串列表。
scores (dict[str, int]): 键为字符串,值为整数的字典。
返回:
tuple[int, float]: 包含整数和浮点数的元组。
"""
print_subheader("2. 类型注解的内置集合类型")
print_info("函数定义使用了 list[str], dict[str, int], tuple[int, float] 注解。")

if not names:
return (0, 0.0)

total_score: int = sum(scores.get(name, 0) for name in names)
average_score: float = total_score / len(names) if names else 0.0

print_success(f"处理的名字: {names}")
print_success(f"处理的分数: {scores}")
print_success(f"返回结果: ({len(names)}, {average_score:.2f})")
return (len(names), average_score)

if __name__ == '__main__':
sample_names: list[str] = ["Alice", "Bob", "Charlie"]
sample_scores: dict[str, int] = {"Alice": 90, "Bob": 85, "David": 70} # Charlie 没有分数
demo_builtin_generic_types(sample_names, sample_scores)

3. 字符串方法:removeprefix()removesuffix()

这两个新的字符串方法用于移除字符串的前缀或后缀,如果存在的话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# === 字符串移除前缀/后缀演示 ===
from print_utils import *

def demo_string_prefix_suffix_removal() -> None:
"""演示 str.removeprefix() 和 str.removesuffix()。"""
print_subheader("3. 字符串方法 `removeprefix()` 和 `removesuffix()`")

filename: str = "document_final_v2.pdf"
url: str = "https://www.example.com/path/to/resource"

# 移除后缀
name_without_suffix: str = filename.removesuffix(".pdf")
print_info(f"'{filename}'.removesuffix('.pdf') -> '{name_without_suffix}'") # 'document_final_v2'

non_matching_suffix: str = filename.removesuffix(".txt") # 后缀不匹配
print_info(f"'{filename}'.removesuffix('.txt') -> '{non_matching_suffix}' (未改变)")

# 移除前缀
path_without_prefix: str = url.removeprefix("https://")
print_info(f"'{url}'.removeprefix('https://') -> '{path_without_prefix}'") # 'www.example.com/path/to/resource'

non_matching_prefix: str = url.removeprefix("http://") # 前缀不匹配
print_info(f"'{url}'.removeprefix('http://') -> '{non_matching_prefix}' (未改变)")

if __name__ == '__main__':
demo_string_prefix_suffix_removal()

Python 3.10 主要特性

Python 3.10 带来了备受期待的结构化模式匹配 (match/case)、更简洁的联合类型表示法以及上下文管理器语法的改进。

1. 结构化模式匹配 (match/case)

这是一种新的控制流语句,允许你根据数据的结构和值来匹配模式,并执行相应的代码块。类似于其他语言中的 switch 或模式匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# === 结构化模式匹配演示 ===
from print_utils import *
from typing import Any, List, Dict

def demo_structural_pattern_matching(data_packet: Any) -> str:
"""演示结构化模式匹配 (match/case) 处理不同数据结构。"""
print_subheader(f"1. 结构化模式匹配 (`match`/`case`) - 输入: {data_packet}")

match data_packet:
case {"type": "user", "id": user_id, "name": str(name_val)}: # 匹配字典结构,并捕获值
return f"用户数据: ID={user_id}, 姓名='{name_val}'"

case {"type": "event", "name": event_name, "details": {"timestamp": ts, **other_details}}: # 嵌套匹配和 **other_details
return f"事件: '{event_name}' @ {ts}, 其他: {other_details}"

case [int(x), int(y)] if x == y: # 匹配列表,包含守卫条件 (guard)
return f"两个相等的整数点: ({x}, {y})"

case [str(op), *numbers] if op in ["SUM", "AVG"]: # 捕获列表剩余部分到 *numbers
result: float = sum(numbers)
if op == "AVG" and numbers:
result /= len(numbers)
return f"操作 '{op}' 在 {numbers} 上的结果: {result:.2f}"

case str() as command if command.lower() == "quit": # 匹配字符串并用 as 绑定
return "收到退出指令。"

case _: # 通配符,匹配任何其他情况
return "未知或不匹配的数据包格式。"

if __name__ == '__main__':
print_header("Python 3.10 特性演示")
packets_to_test: List[Any] = [
{"type": "user", "id": 101, "name": "Alice"},
{"type": "event", "name": "login", "details": {"timestamp": "2025-05-18T10:00:00Z", "source_ip": "192.168.1.1"}},
[10, 10],
[5, 7],
["SUM", 1, 2, 3, 4, 5],
["AVG", 10, 20, 30],
"quit",
42
]
for i, packet in enumerate(packets_to_test):
print_info(f"处理包 {i+1}:")
print_success(f" 结果: {demo_structural_pattern_matching(packet)}")

2. 联合类型操作符 (|)

现在可以使用 | 操作符来表示联合类型 (Union),使得类型注解更简洁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# === 联合类型操作符演示 ===
from print_utils import *
# from typing import Union, Optional # 在 Python 3.10+ 中 Optional 和 Union 不再必需显式导入来使用 |

def demo_union_operator(value: int | str | float) -> None: # 使用 | 代替 Union[int, str, float]
"""演示使用 | 作为联合类型操作符。"""
print_subheader("2. 联合类型操作符 (`|`)")

print_info(f"接收到的值: {value} (类型: {type(value).__name__})")
if isinstance(value, (int, float)):
print_success(f" 数值处理: {value * 2}")
elif isinstance(value, str):
print_success(f" 字符串处理: '{value.upper()}'")

def process_optional_data(data: str | None = None) -> None: # 使用 | None 代替 Optional[str]
"""演示使用 | None 代替 Optional。"""
if data is None:
print_warning(" 未提供数据。")
else:
print_success(f" 处理可选数据: '{data}'")

if __name__ == '__main__':
demo_union_operator(100)
demo_union_operator("hello python 3.10")
demo_union_operator(3.14)
process_optional_data("Some data")
process_optional_data() # data 为 None

3. 上下文管理器改进:括号内多个 with 表达式

现在可以在 with 语句后的括号内编写多个上下文表达式,而无需嵌套 with 语句,使代码更扁平。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# === 上下文管理器改进演示 ===
from print_utils import *
import os # 用于文件操作

def demo_multiple_context_expressions() -> None:
"""演示在 with 语句中使用括号包含多个上下文表达式。"""
print_subheader("3. 上下文管理器改进 (括号内多个表达式)")

input_filename: str = "temp_input_for_with.txt"
output_filename: str = "temp_output_for_with.txt"

# 创建临时输入文件
with open(input_filename, "w", encoding="utf-8") as f_in_setup:
f_in_setup.write("第一行数据。\n第二行数据。\n")
print_info(f"已创建临时输入文件: {input_filename}")

try:
# Python 3.10+ 的写法
with (
open(input_filename, "r", encoding="utf-8") as infile,
open(output_filename, "w", encoding="utf-8") as outfile
):
print_info(f" 同时打开 '{infile.name}' (读) 和 '{outfile.name}' (写)。")
content: str = infile.read()
outfile.write(content.upper()) # 示例操作:转为大写后写入
print_success(f" 内容已从输入文件读取,处理后写入输出文件。")

# 验证输出文件
if os.path.exists(output_filename):
with open(output_filename, "r", encoding="utf-8") as f_check:
print_info(f" 输出文件 '{output_filename}' 内容:\n'''\n{f_check.read().strip()}\n'''")

except Exception as e:
print_error(f" 处理文件时发生错误: {e}")
finally:
# 清理临时文件
if os.path.exists(input_filename): os.remove(input_filename)
if os.path.exists(output_filename): os.remove(output_filename)
print_info("临时文件已清理。")

if __name__ == '__main__':
demo_multiple_context_expressions()

Python 3.11 主要特性

Python 3.11 带来了显著的性能提升 (CPython Faster计划),以及异常组、异常注释、Self 类型和 LiteralString 等新特性。

1. 异常组 (ExceptionGroup) 和异常注释 (add_note())

ExceptionGroup 允许同时处理多个不相关的异常。add_note() 方法可以向异常对象添加上下文注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# === 异常组和异常注释演示 ===
from print_utils import *
# from exceptiongroup import ExceptionGroup # 在 Python 3.11+ 中,ExceptionGroup 是内置的
from typing import List

def demo_exception_groups_and_notes() -> None:
"""演示 ExceptionGroup 和 e.add_note()。"""
print_subheader("1. 异常组 (`ExceptionGroup`) 和异常注释 (`add_note()`)")

# 演示 add_note()
print_info("演示 e.add_note():")
try:
user_input: str = "non_numeric_value"
value: int = int(user_input)
except ValueError as e:
e.add_note(f"输入的值 '{user_input}' 无法转换为整数。") # 添加注释
e.add_note("请确保输入的是有效的数字。")
# raise # 如果要重新引发带注释的异常
print_warning(f"捕获到 ValueError (带注释):")
# 要查看注释,你需要捕获异常并访问其 __notes__ 属性,或让它传播到顶层由解释器打印
# print(f" 异常: {e}")
# if hasattr(e, "__notes__"):
# for note in e.__notes__:
# print(f" Note: {note}")
# Python 3.11 的默认异常回溯会显示注释
try:
raise e # 重新引发以观察其默认打印效果
except ValueError as e_raised:
print_error(f" 重新引发的异常 (其回溯应包含注释):\n{e_raised}")
if hasattr(e_raised, "__notes__"): # Python 3.11+
print_info(" 异常包含的注释 (__notes__):")
for note_item in e_raised.__notes__:
print(f" - {note_item}")


# 演示 ExceptionGroup (概念性)
print_info("\n演示 ExceptionGroup (概念性):")
# 假设我们有多个并发任务,每个都可能失败
exceptions_list: List[Exception] = []
task1_result, task2_result = None, None

# 模拟任务1
try:
# task1_result = 10 / 0
print_info(" 任务1: 模拟成功。")
task1_result = "Success"
except ZeroDivisionError as e1:
e1.add_note("任务1 (除法运算) 失败。")
exceptions_list.append(e1)
print_warning(" 任务1: 捕获 ZeroDivisionError。")


# 模拟任务2
try:
# data = {}
# value = data["missing_key"]
print_info(" 任务2: 模拟成功。")
task2_result = "OK"
except KeyError as e2:
e2.add_note("任务2 (字典键查找) 失败。")
exceptions_list.append(e2)
print_warning(" 任务2: 捕获 KeyError。")

if exceptions_list:
# 如果有多个异常,可以用 ExceptionGroup 包装它们
# 在 Python 3.11+ 中,ExceptionGroup 是内置的
# from exceptiongroup import ExceptionGroup # 对于旧版本,可能需要此库
error_group = ExceptionGroup("在执行多个任务时发生错误", exceptions_list)
print_error(f" 发生了异常组: {error_group}")
# 处理异常组:
# try:
# raise error_group
# except* ValueError as eg_vg: # Python 3.11+ 的 except* 语法
# print_warning(f" 捕获到异常组中的 ValueError: {eg_vg.exceptions}")
# except* TypeError as eg_tg:
# print_warning(f" 捕获到异常组中的 TypeError: {eg_tg.exceptions}")
print_info(" (在实际代码中,会使用 try...except* 来处理 ExceptionGroup)")
else:
print_success(" 所有模拟任务均成功完成。")


if __name__ == '__main__':
print_header("Python 3.11 特性演示")
demo_exception_groups_and_notes()

2. Self 类型

typing.Self (或仅 Self,如果 from typing import Self) 提供了一种更简洁、更准确的方式来注解那些返回类自身实例的方法 (例如构造函数、工厂方法、或返回修改后自身的链式调用方法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# === Self 类型演示 ===
from print_utils import *
from typing import Self # Python 3.11+ (或 typing_extensions.Self for older)

def demo_self_type() -> None:
"""演示 typing.Self 类型的使用。"""
print_subheader("2. `Self` 类型")

class ConfigBuilder:
def __init__(self, name: str):
self.name: str = name
self.settings: dict = {}
print_info(f" ConfigBuilder '{name}' 已创建。")

def set_option(self, key: str, value: Any) -> Self: # 返回类型是 Self
"""设置一个配置选项并返回自身,以便链式调用。"""
self.settings[key] = value
print_info(f" 选项 '{key}' 设置为 '{value}'")
return self # 返回当前类的实例

@classmethod
def create_default(cls, name: str) -> Self: # 类方法返回 Self
"""创建一个带有默认设置的实例。"""
print_info(f" 调用类方法 create_default for '{name}'")
instance = cls(name) # cls 指向 ConfigBuilder (或其子类)
instance.set_option("default_timeout", 30)
return instance

def build(self) -> dict:
print_info(f" 构建配置 '{self.name}'...")
return {"name": self.name, "settings": self.settings}

# 使用示例
builder1: ConfigBuilder = ConfigBuilder("App1")
builder1.set_option("port", 8080).set_option("retries", 3) # 链式调用
config1: dict = builder1.build()
print_success(f" Config1: {config1}")

builder2: ConfigBuilder = ConfigBuilder.create_default("App2_Default")
builder2.set_option("theme", "dark")
config2: dict = builder2.build()
print_success(f" Config2 (from default): {config2}")

# 演示 Self 在子类中的行为
class AdvancedConfigBuilder(ConfigBuilder):
def enable_feature_x(self) -> Self: # 返回的仍然是 AdvancedConfigBuilder 类型
self.set_option("feature_x", True)
return self

adv_builder = AdvancedConfigBuilder("AdvApp").enable_feature_x().set_option("mode", "expert")
adv_config = adv_builder.build()
print_success(f" AdvancedConfig: {adv_config}")
print_info(f" adv_builder 的类型是: {type(adv_builder).__name__} (应为 AdvancedConfigBuilder)")


if __name__ == '__main__':
demo_self_type()

3. LiteralString 类型

typing.LiteralString 用于注解那些值必须是字面量字符串(即直接在代码中写出的字符串,而不是通过变量拼接或函数调用生成的字符串)的参数。这有助于静态分析工具检测潜在的安全风险,例如 SQL 注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# === LiteralString 类型演示 ===
from print_utils import *
from typing import LiteralString # Python 3.11+

def demo_literal_string_type() -> None:
"""演示 typing.LiteralString 类型。"""
print_subheader("3. `LiteralString` 类型")

def execute_safe_query(query: LiteralString, params: tuple = ()) -> None:
"""
一个模拟的安全数据库查询函数。
它只接受字面量字符串作为查询语句,以防止SQL注入。
"""
print_info(f" 尝试执行安全查询: '{query}' (参数: {params})")
# 在实际应用中,这里会与数据库交互,并可能使用参数化查询
print_success(f" 模拟执行查询: {query} (参数化: {params})")

# 正确的用法:传递字符串字面量
execute_safe_query("SELECT * FROM users WHERE status = 'active'")
execute_safe_query("SELECT name, email FROM customers WHERE id = %s", params=(101,))

# 错误的用法:传递通过变量或拼接生成的字符串
# 静态类型检查器 (如 MyPy) 会对以下调用发出警告
table_name_var: str = "users"
# execute_safe_query(f"SELECT * FROM {table_name_var}") # MyPy 会警告: Expected LiteralString

user_input_column: str = input("请输入要查询的列名 (例如 'username'): ") if os.getenv("ASK_INPUT") else "username" # 模拟用户输入
# execute_safe_query(f"SELECT {user_input_column} FROM data") # MyPy 会警告

print_warning(" 注意: LiteralString 主要用于静态类型检查。")
print_info(" 如果上面的注释行被取消且使用了 MyPy,它会标记出潜在的类型错误。")
print_info(" 在运行时,Python 本身不强制 LiteralString。")


if __name__ == '__main__':
# os.environ["ASK_INPUT"] = "1" # 取消注释以在运行时测试 input() 部分 (可选)
demo_literal_string_type()
# if "ASK_INPUT" in os.environ: del os.environ["ASK_INPUT"]

Python 3.12 主要特性

Python 3.12 继续改进类型系统,带来了更简洁的泛型类和函数语法,以及 f-string 的进一步增强。

1. 泛型类和函数的简化语法 (PEP 695)

现在可以使用更简洁的方括号语法直接在类或函数定义中声明类型参数,而无需从 typing 导入 TypeVar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# === 简化泛型语法演示 ===
from print_utils import *
# from typing import TypeVar # 在 Python 3.12+ 中 TypeVar 可以通过新语法隐式定义

def demo_simplified_generics_syntax() -> None:
"""演示 Python 3.12+ 中简化的泛型类和函数语法。"""
print_subheader("1. 泛型类和函数的简化语法 (PEP 695)")

# 简化泛型函数定义
# 旧方法: T = TypeVar('T'); def old_first(items: list[T]) -> T | None: ...
def new_first[T](items: list[T]) -> T | None: # 直接在函数名后用 [] 声明类型参数 T
"""获取列表的第一个元素,如果列表为空则返回 None。"""
print_info(f" new_first 调用,items: {items}")
return items[0] if items else None

print_success(f" new_first([10, 20]): {new_first([10, 20])}")
print_success(f" new_first(['a', 'b']): {new_first(['a', 'b'])}")
print_success(f" new_first([]): {new_first([])}")

# 简化泛型类定义
# 旧方法: U = TypeVar('U'); class OldBox(Generic[U]): ...
class NewBox[U]: # 直接在类名后用 [] 声明类型参数 U
def __init__(self, content: U):
self.content: U = content

def get_content(self) -> U:
return self.content

def __repr__(self) -> str:
return f"NewBox[{type(self.content).__name__}](content={self.content!r})"

int_box = NewBox[int](123) # 可以显式指定类型
str_box = NewBox("Python 3.12") # 也可以让类型检查器推断

print_success(f" {int_box} -> content: {int_box.get_content()}")
print_success(f" {str_box} -> content: {str_box.get_content()}")

if __name__ == '__main__':
print_header("Python 3.12 特性演示")
demo_simplified_generics_syntax()

2. 类型别名与类型变量语法改进 (type 语句) (PEP 695)

Python 3.12 引入了 type 语句作为创建类型别名和类型变量的更清晰、更正式的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# === type 语句演示 ===
from print_utils import *
from typing import List # List 仍可用于兼容性或复杂场景

# Python 3.12+ 的 type 语句
# 定义类型别名
type Point = tuple[float, float]
type IntList = list[int]
# 定义类型变量 (TypeVar)
type T = str | int # T 现在是一个 TypeVar,可以绑定到 str 或 int
type K_contra = contravariant str # 定义逆变类型变量 K,上界为 str
type V_co = covariant int # 定义协变类型变量 V,上界为 int

def demo_type_statement() -> None:
"""演示 Python 3.12+ 中使用 type 语句定义类型别名和类型变量。"""
print_subheader("2. 类型别名与类型变量语法改进 (`type` 语句)")

def process_point(p: Point) -> None:
print_info(f" 处理点: {p}, 类型: {type(p)}")

def sum_int_list(numbers: IntList) -> int:
return sum(numbers)

def process_generic_var(item: T) -> None: # T 是 TypeVar (str | int)
if isinstance(item, str):
print_info(f" 泛型变量 T (字符串): {item.upper()}")
elif isinstance(item, int):
print_info(f" 泛型变量 T (整数): {item * 100}")

my_point: Point = (1.0, 2.5)
process_point(my_point)
# process_point([1.0, 2.5]) # MyPy 会报错,类型不匹配

my_numbers: IntList = [10, 20, 30]
print_success(f" 整数列表 {my_numbers} 的和: {sum_int_list(my_numbers)}")

process_generic_var("hello")
process_generic_var(5)
# process_generic_var(3.14) # MyPy 会报错,因为 T 只能是 str 或 int

if __name__ == '__main__':
demo_type_statement()

3. f-string 语法进一步改进 (PEP 701)

Python 3.12 中的 f-string 更加强大和灵活,主要体现在:

  • 引号重用:现在可以在 f-string 表达式内部重用与包围 f-string 自身所用类型相同的引号,无需转义。
  • 多行表达式和注释:f-string 表达式部分现在可以跨越多行,并且可以包含 # 注释。
  • 反斜杠:表达式部分可以使用反斜杠。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# === f-string 语法改进演示 ===
from print_utils import *

def demo_fstring_enhancements() -> None:
"""演示 Python 3.12+ 中 f-string 的语法改进。"""
print_subheader("3. f-string 语法进一步改进 (PEP 701)")

user_data: dict = {"name": "Alice", "city": "Wonderland"}
user_id: int = 101

# 引号重用
# 在 3.12 之前: f"User data: {user_data['name']}" (需要不同类型的引号或转义)
# 或者 f'User data: {user_data["name"]}'
message_quotes: str = f"User: {user_data['name']}" # 3.12+: 可以重用引号
print_success(f" 引号重用: {message_quotes}")

message_quotes_single: str = f'ID: {user_data.get("name", "N/A")!r}' # 外层单引号,内部仍可方便使用双引号
print_success(f" 引号重用 (单引号): {message_quotes_single}")


# 多行表达式和注释
# (注意:在多行 f-string 表达式中,注释必须在自己的行上)
items_list: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
formatted_multiline: str = (
f"Data summary for user {user_id}:\n"
f" - Items count: {len(items_list)}\n"
f" - Even items sum: {sum(
x
for x in items_list
if x % 2 == 0 # 这是一个 f-string 表达式内部的注释
)}\n" # 多行表达式
f" - User info: {user_data['name'].upper() # .upper() 方法调用
+ ' from ' + user_data['city']}"
)
print_info(" f-string 带多行表达式和注释:")
print(formatted_multiline)

# 反斜杠 (虽然不常见,但现在允许)
# path_example = f"Path: {'C:\\Users\\Default'}" # 原本就这样可以
# fstring_backslash = f"{'a\\nb'.split('\\n')}" # 现在更灵活
# print_success(f" f-string 中使用反斜杠: {fstring_backslash}")


if __name__ == '__main__':
demo_fstring_enhancements()

Python 3.13 主要特性

Python 3.13 带来了包括新的 @override 装饰器、JIT 编译器的初步实验(可能不直接体现在日常语法中)、以及对现有特性的一些改进。

1. @typing.override 装饰器 (PEP 698)

一个新的装饰器 @override (来自 typing 模块,或 Python 3.12+ 的 typing_extensions 中已有) 用于明确指示一个方法意图覆盖其父类中的同名方法。这有助于静态类型检查器和开发者捕捉由于意外拼写错误或签名不匹配导致的覆盖失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# === @override 装饰器演示 ===
from print_utils import *
from typing import override # Python 3.13+ (或 from typing_extensions import override for 3.12)

def demo_override_decorator() -> None:
"""演示 @typing.override 装饰器。"""
print_subheader("1. `@typing.override` 装饰器 (PEP 698)")

class BaseDocument:
def get_title(self) -> str:
return "Generic Document"

def render_content(self) -> str:
raise NotImplementedError

class Article(BaseDocument):
def __init__(self, title: str, body: str):
self._title = title
self._body = body

@override # 明确表示此方法覆盖父类方法
def get_title(self) -> str:
return f"Article: {self._title}"

@override # 明确表示此方法覆盖父类方法
def render_content(self) -> str:
return f"<h1>{self.get_title()}</h1><p>{self._body}</p>"

# 如果写成 @override def get_titel(self) -> str: ... (拼写错误)
# MyPy 这类类型检查器会报错,因为它没有覆盖任何父类方法。

my_article = Article("Python 3.13 News", "Override decorator is here!")
print_success(f" Article Title: {my_article.get_title()}")
print_info(f" Rendered Article (simulated):\n{my_article.render_content()}")

# 尝试覆盖一个不存在的基类方法 (MyPy会警告)
# class BadSubclass(BaseDocument):
# @override
# def non_existent_method_in_base(self) -> None:
# pass

print_info(" @override 帮助在静态检查时发现覆盖错误。")

if __name__ == '__main__':
print_header("Python 3.13 (部分) 特性演示")
demo_override_decorator()