Python 3.15 中两个有用的小更新

2026 年 10 月 9 日,Python 正式发布了 3.15 版本。主要更新内容包括引入了显式延迟导入机制 lazy import(PEP 810)、新增不可变字典类型 frozendict(PEP 814)、新增专用哨兵类型 sentinel(PEP 661)、在推导式中支持解包语法(PEP 798)、将 UTF-8 设为默认文本编码(PEP 686)、新增 Tachyon 性能分析器(PEP 799)等。

本文将重点介绍两个小更新:列表推导式支持解包语法,以及用 re.prefixmatch() 取代 re.match()。

1. 列表推导式支持解包符号

你有没有曾经尝试过在列表推导式中使用星号(*)来展开嵌套列表?

nested_list = [[1, 2], [3, 4], [5, 6]]
[*sublist for sublist in nested_list]
#   File "<python-input-0>", line 2
#     [*sublist for sublist in nested_list]
#      ^^^^^^^^
# SyntaxError: iterable unpacking cannot be used in comprehension

在 Python 3.14 及以下版本中尝试这样的操作会发生报错 SyntaxError。Python 并不支持这种用法。

而在 Python 3.15 中,PEP 798 引入了在列表推导式内部进行解包的功能(也包括元组、字典、集合和生成器推导式)。因此,现在可以使用这种非常自然的语法来展开嵌套列表、元组、集合、字典了。

解包列表:

nested_list = [[1, 2], [3], [4, 5]]
[*sublist for sublist in nested_list]
# [1, 2, 3, 4, 5]

解包元组、构建生成器:

nested_tuple = ((1, 2), (3,), (4, 5))
(*subtuple for subtuple in nested_tuple)  # Generator expression
# <generator object <genexpr> at 0x10ab7c3c0>
tuple(*subtuple for subtuple in nested_tuple)
# (1, 2, 3, 4, 5)

解包集合:

nested_set = [{1, 2}, {3}, {4, 5}]
{*subset for subset in nested_set}
# {1, 2, 3, 4, 5}

解包字典(使用双星号 **):

nested_dict = [{"a": 1, "b": 2}, {"c": 3}, {"d": 4, "e": 5}]
{**subdict for subdict in nested_dict}
# {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

以前的一些做法

以这个输入数据为例:

its = [[1, 2], [3], [4, 5]]
dicts = [{"a": 1, "b": 2}, {"c": 3}, {"d": 4, "e": 5}]
  1. 显式循环

最直接的方式是创建空容器,然后逐个把内层元素添加进去:

new_list = []
for it in its:
    new_list.extend(it)

new_set = set()
for it in its:
    new_set.update(it)

new_dict = {}
for d in dicts:
    new_dict.update(d)

def new_generator():
    for it in its:
        yield from it

new_list               # [1, 2, 3, 4, 5]
new_set                # {1, 2, 3, 4, 5}
new_dict               # {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
list(new_generator())  # [1, 2, 3, 4, 5]

extend() 和 update() 都是原地修改;yield from 则把内层可迭代对象逐个产出。

  1. 推导式(两层循环)

可以把这些循环写成推导式,先遍历外层 its/dicts,再遍历每个 it/d 中的元素。

[x for it in its for x in it]
# [1, 2, 3, 4, 5]

{x for it in its for x in it}
# {1, 2, 3, 4, 5}

{key: value for d in dicts for key, value in d.items()}
# {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

(x for it in its for x in it)
# <generator object ...>
  1. 借助 itertools

itertools.chain 专门用来串联多个可迭代对象:

import itertools

list(itertools.chain(*its))
# [1, 2, 3, 4, 5]

set(itertools.chain(*its))
# {1, 2, 3, 4, 5}

dict(itertools.chain(*(d.items() for d in dicts)))
# {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

itertools.chain(*its)
# <itertools.chain object ...>

chain(*its) 把 its 拆成多个参数;如果 its 本身是一个生成器或很大的列表,可以用 chain.from_iterable():

list(itertools.chain.from_iterable(its))
# [1, 2, 3, 4, 5]

set(itertools.chain.from_iterable(its))
# {1, 2, 3, 4, 5}

dict(itertools.chain.from_iterable(d.items() for d in dicts))
# {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

itertools.chain.from_iterable(its)
# <itertools.chain object ...>
  1. 借助 functools.reduce

对列表、集合和字典,还可以用 functools.reduce 配合原地运算符:

import functools
import operator

sets = [{1, 2}, {3}, {4, 5}]

functools.reduce(operator.iconcat, its, (new_list := []))
# [1, 2, 3, 4, 5]

functools.reduce(operator.ior, sets, (new_set := set()))
# {1, 2, 3, 4, 5}

functools.reduce(operator.ior, dicts, (new_dict := {}))
# {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

operator.iconcat 对应列表的 +=,operator.ior 对应集合或字典的 |=。:= 把初始累加器绑定到 new_list、new_set、new_dict,方便之后查看结果。

2. 用 re.prefixmatch() 替代 re.match()

Python 3.15 正式引入了 re.prefixmatch(),并将原有的 re.match() 标记为“软弃用”(soft deprecated)。之所以是“软弃用”,是因为这并非功能上的弃用和替代,而是一次旨在消除语义歧义的 API 命名调整,是为了符合 Python 之禅中“显式优于隐式”的原则。

如果你用过 Python 的正则表达式模块 re,你很可能见过或用过 re.match(),尽管你可能没有充分理解它实际的行为。

具体来说,Python 的 re.match() 实际上只尝试从字符串的起始位置进行匹配,而不会搜索整个字符串。但在许多其他编程语言和正则表达式库中,match 通常表示查找字符串中符合模式的内容,其语义更接近 Python 的 re.search()。

import re

text = "hello python"

re.match("python", text)        # None
re.prefixmatch("python", text)  # None
re.match("hel", text)           # 匹配成功
re.search("python", text)       # 匹配成功

这种行为毫无疑问和“match”的语义常识不符,也引发了相关的社区讨论。许多人表示曾遇到过这个问题。出于兼容性考虑,Python 引入了新的名字 re.prefixmatch() 并推荐使用。