page contents

Python 3.15:那些未被关注但实用的新特性

随着 Python 3.15.0b1 进入功能冻结阶段,我们已经能窥见今年 Python 的更新全貌。此次版本包含多项重磅特性,其中就包括我此前介绍过的惰性导入(lazy imports)与超光速分析器(tachyon profiler)。

attachments-2026-05-zPli0c3p6a1650a8e9790.png

又到了一年一度的新时刻,全新版本的 Python 即将面世。

随着 Python 3.15.0b1 进入功能冻结阶段,我们已经能窥见今年 Python 的更新全貌。此次版本包含多项重磅特性,其中就包括我此前介绍过的惰性导入(lazy imports)与超光速分析器(tachyon profiler)。

在以前,我们曾深入探究了 Python 3.14 的小众特性,并且乐在其中。我发现,这些特性与那些备受瞩目的 PEP 提案同样精彩,值得更多关注。今年亦是如此。

异步任务组取消机制(Asyncio Taskgroup Cancellation)

 

本次版本中,异步 I/O(Asyncio)模块的改动不多,核心更新是优雅取消任务组(TaskGroup) 的能力。

任务组是结构化并发的一种实现,让开发者能以简洁方式创建多个并发任务。

asyncwith asyncio.TaskGroup()as tg:
    tg.create_task(run())
    tg.create_task(run())

# 等待所有任务执行完毕

假设我们需要在后台等待某种信号,以中断任务组执行 —— 在异步 I/O 中,这看似简单,实际实现却颇为繁琐。

class Interrupt(Exception):
pass

with suppress(Interrupt):
asyncwith asyncio.TaskGroup()as tg:
        tg.create_task(run())
        tg.create_task(run())
ifawait wait_for_signal():
raise Interrupt()

上述代码能够生效,是因为任务组内抛出的异常会触发其他任务取消。自定义Interrupt异常会被封装在异常组(ExceptionGroup)中,再由contextlib.suppress捕获过滤,最终实现优雅退出。

suppress对异常组的兼容处理,本身就是 Python 3.12 中一项被忽视的特性。

撰写本文时,我也是偶然发现到的这一点。

Python 3.15 新增的TaskGroup.cancel方法,将这一过程大幅简化:

asyncwith asyncio.TaskGroup()as tg:
    tg.create_task(run())
    tg.create_task(run())
ifawait wait_for_signal():
        tg.cancel()

 

相较以往,如今的写法简洁至极,无需过多解释 —— 直接取消整个任务组,且不抛出任何异常。

上下文管理器优化

 

装饰器的编写难度颇高,甚至已经成为面试高频的考点。但你是否知道,上下文管理器也可直接作为装饰器使用?

from contextlib import contextmanager
import time
from typing import Iterator

@contextmanager
def duration(message:str)-> Iterator[None]:
    start = time.perf_counter()
try:
yield
finally:
print(f"{message} 耗时 {time.perf_counter()- start:.2f} 秒")

 

以上是常用的上下文管理器,用于打印代码块执行时长。自 Python 3.3 起,它可直接作为装饰器:

@duration("任务执行")
def workload():
# 业务逻辑

# 也可直接作为包装器调用
duration("其他任务")(other_workload)

这种用法虽然便捷,但存在局限性,无法适用于迭代器、异步函数与异步迭代器:

@duration("异步任务")
asyncdefasync_workload():
# 异步业务逻辑

@duration("生成器任务")
def workload():
while True:
yield

原因在于,这类对象与普通函数语义不同:调用后会立即返回生成器、协程函数或异步生成器对象,导致装饰器执行瞬间结束,无法覆盖整个函数生命周期。

这一问题困扰了我许久,普通装饰器也常存在类似问题。Python 3.15 对此做出优化:ContextDecorator会自动检测被装饰函数类型,确保装饰器覆盖完整生命周期。

在我看来,这让上下文管理器成为编写装饰器的最佳方式—— 规避常见陷阱,语法更简洁。强烈推荐大家尝试这种用法。

线程安全迭代器

 

迭代器是现代 Python 的核心基础,它将数据源与数据消费逻辑解耦,实现简洁抽象:

from typing import Iterator

def stream_events()-> Iterator[str]:
whileTrue:
yield blocking_get_event()

events = stream_events()
for event in events:
    consume(event)

 

但在多线程 / 自由线程场景下,这一抽象会失效:迭代器默认非线程安全,可能出现数据丢失、内部状态错乱等问题。

Python 3.15 通过threading.serialize_iterator解决该问题,直接包裹原迭代器即可:

import threading
from concurrent.futures import ThreadPoolExecutor

events = threading.serialize_iterator(stream_events())

with ThreadPoolExecutor()as executor:
    fut1 = executor.submit(consume, events)
    fut2 = executor.submit(consume, events)

 

此外,新增threading.synchronized_iterator装饰器,可直接作用于生成器函数,自动包裹迭代器结果。

同时提供threading.concurrent_tee,与原生tee不同,它会为多个迭代器复制完整数据流,而非拆分数据:

source1, source2 = threading.concurrent_tee(squares(10), n=2)

with ThreadPoolExecutor()as executor:
    fut1 = executor.submit(consume, source1)
    fut2 = executor.submit(consume, source2)

此前,多线程环境下需依赖队列(Queue)同步数据消费;如今新增工具,无需修改原有抽象,即可适配多线程场景。

附加特性

 

以往时刻我仅介绍 3 项小众特性,今年值得关注的更新更多。以下 2 项虽影响范围有限,但同样趣味十足。

计数器异或运算(Counter xor Operation)

 

collections.Counter是实用工具类,用于统计离散元素频次,行为类似dict[键类型, 整数],并内置丰富运算:

from collections import Counter

c = Counter(a=3, b=1)
d = Counter(a=1, b=2)

print(f"{c + d =}")# 合并计数:对应键值相加
print(f"{c - d =}")# 差值计数:仅保留正数

输出:

c + d = Counter(a=4, b=3)
c - d = Counter(a=1, b=0)

同时支持集合式运算:

print(f"{c & d =}")# 交集:取对应键最小值
print(f"{c | d =}")# 并集:取对应键最大值

输出:

c & d = Counter(a=1, b=1)
c | d = Counter(a=3, b=2)

可将Counter理解为离散元素集合,上述运算等价于:

{a_0, a_1, a_2, b_0} & {a_0, b_0, b_1} == {a_0, b_0}
{a_0, a_1, a_2, b_0} | {a_0, b_0, b_1} == {a_0, a_1, a_2, b_0, b_1}

Python 3.15 新增异或运算(xor):

c = Counter(a=3, b=1)
d = Counter(a=1, b=2)

# 异或 = 并集 - 交集

c ^ d == c | d - c & d == Counter(a=3, b=2)- Counter(a=1, b=1)== Counter(a=2, b=1)

用集合表示更清晰:

{a_0, a_1, a_2, b_0} ^ {a_0, b_0, b_1} == {a_1, a_2, b_1}

我将其归入附加特性,因我从未在实际场景中对Counter使用集合运算,也难以想到异或运算的应用场景。但为了功能完整性,开发者新增了这一特性,值得肯定。

不可变 JSON 对象

 

Python 3.15 新增frozendict类型,至此,所有 JSON 数据类型(数组、布尔值、浮点数、空值、字符串、对象)均支持 ** 不可变(可哈希)** 表示形式。

json.load与json.loads新增 **array_hook参数 **,与object_hook参数互补,可直接将 JSON 解析为不可变对象:

import json
from types import MappingProxyType

# 解析为不可变字典+元组
json.loads('{"a": [1, 2, 3, 4]}', array_hook=tuple, object_hook=dict)== MappingProxyType({'a':(1,2,3,4)})

结语

相较于版本更新里备受热议的重磅功能,这些低调的优化看似波澜不惊,却精准补齐日常开发中的各类细节短板。

从异步任务管控、装饰器机制完善,到多线程并发适配、数据运算与格式处理升级,每一处改动都贴合开发者实际编码需求。

可以看到,Python 始终在兼顾性能、易用性与稳定性的道路上稳步迭代,细碎的功能打磨持续夯实语言根基,也为开发者带来更流畅稳健的编码体验。

 更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2071 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 2071 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章