Skip to content

What’s New In Python 3.12

Important

  1. venv 创建的虚拟环境中不再预安装setuptools
  2. 垃圾回收在执行循环中的eval breaker时触发, 而不是分配对象时
  3. inspect.getattr_static() 支持在不触发描述符的方式静态查找一个对象的属性

PEP 695: type parameter syntax and the type statement

Generic

支持新的泛型语法, 在函数声明里定义泛型并在函数签名注解里使用

Example
def max_value[T](values: Iterable[T]) -> T:
    ...

Alias

支持typedef效果的别名定义

Example
type Point = tuple[float, float]

PEP701: f-strings

引号使用优化

大括号内外允许使用相同的单引号或双引号

Example
print(f"{"hello" + " " + "world"}")

跨行

在大括号中允许换行

Example
def add(a, b):
    return a + b

print(f"Sum is: {
    add(
        10,
        20
    )
}")

PEP 669: low impact monitoring

引入了低开销的监控 API, 对于调试、性能分析等有很大帮助

Example
import sys
import os.path

# 定义调试器工具ID
DEBUGGER_ID = sys.monitoring.DEBUGGER_ID

# 初始化跟踪,开启PY_START事件(函数执行开始)
def init_tracing():
    sys.monitoring.use_tool_id(DEBUGGER_ID, "pep669_based_debugger")
    sys.monitoring.set_events(DEBUGGER_ID, sys.monitoring.events.PY_START)

# PY_START事件的回调函数,打印调用的函数名和行号
def pep_669_py_start_trace(code, instruction_offset):
    frame = sys._getframe(1)
    print("calling {} in {} at line {}".format(
        code.co_name,
        os.path.basename(code.co_filename),
        frame.f_lineno)
    )

# 注册PY_START回调
sys.monitoring.register_callback(DEBUGGER_ID, sys.monitoring.events.PY_START, pep_669_py_start_trace)

def f():
    pass

def g():
    f()

def h():
    g()

if __name__ == '__main__':
    init_tracing()
    h()

PEP 688: buffer protocol

通过 __buffer____release_buffer__ 让类支持 memoryview 实例化管理

Example
import contextlib
import inspect

class MyBuffer:
    def __init__(self, data: bytes):
        self.data = bytearray(data)
        self.view = None

    def __buffer__(self, flags: int) -> memoryview:
        if flags != inspect.BufferFlags.FULL_RO:
            raise TypeError("Only BufferFlags.FULL_RO supported")
        if self.view is not None:
            raise RuntimeError("Buffer already held")
        self.view = memoryview(self.data)
        return self.view

    def __release_buffer__(self, view: memoryview) -> None:
        assert self.view is view
        self.view.release()
        self.view = None

    def extend(self, b: bytes) -> None:
        if self.view is not None:
            raise RuntimeError("Cannot extend held buffer")
        self.data.extend(b)

buffer = MyBuffer(b"capybara")

with memoryview(buffer) as view:
    view[0] = ord("C")

with contextlib.suppress(RuntimeError):
    buffer.extend(b"!")

with memoryview(buffer) as view:
    assert view.tobytes() == b"Capybara!"

Type Hints

  1. 使用 TypedDict 注解 **kwargs

PEP698: typing.override

  1. 通过 typing.override 注解子类重写了父类的该方法
  2. 如果装饰的函数在父类中不存在, 静态类型检查器会报错
Example
from typing import override

class Base:
    def foo(self) -> int:
        return 1

class Derived(Base):
    @override
    def foo(self) -> int:
        return 2

asyncio.eager_task_factory

允许在 asyncio 中修改事件循环调度机制, 在创建任务立即执行, 而不是将任务放入调度队列等待下一次调度

Example
import asyncio
import time

async def quick_coro():
    print("开始协程")
    await asyncio.sleep(0.1)  # 模拟异步等待
    print("协程结束")
    return 42

async def main():
    loop = asyncio.get_running_loop()

    # 设置事件循环工厂为 eager_task_factory
    loop.set_task_factory(asyncio.eager_task_factory)

    print("=== 使用 eager_task_factory 创建任务 ===")
    task = loop.create_task(quick_coro())
    print("任务创建后立刻执行至第一个 await")
    result = await task
    print(f"任务完成,结果: {result}")

    # 恢复默认工厂以便对比
    loop.set_task_factory(None)

    print("\n=== 使用默认 create_task ===")
    task2 = asyncio.create_task(quick_coro())
    print("任务创建后不会立即执行")
    result2 = await task2
    print(f"任务完成,结果: {result2}")

asyncio.run(main())

Nice

  1. asyncio 内部的性能提升
  2. uuid库支持命令行使用 python -m uuid -u uuid4
  3. PEP623, 每个str对象至少减少了 8 个字节
  4. itertools.batched 返回对序列按长度划分为多个元组的迭代器
  5. 添加 pathlib.Path.walk(), 行为类似 os.walk
  6. pathlib.Path.glob()pathlib.Path.rglob()pathlib.PurePath.match() 添加 case_sensitive 可选参数以忽略大小写
  7. shutil.rmtree() 支持 通过onexc回调函数处理删除目录失败的情况
  8. tempfile.NamedTemporaryFile 支持 delete_on_close 参数控制在退出上下文时是否删除文件
  9. 增加 threading.settrace_all_threads()threading.setprofile_all_threads() 用于为所有线程设置函数触发回调
  10. 使用isinstance(obj, collections.abc.Buffer) 检测对象是否支持缓冲区协议

Trivial

  1. PEP 684, a unique per-interpreter GIL 每个解释器单独拥有一个 GIL 锁, 提升多核利用能力
  2. pathlib.Path 支持被继承
  3. tokenize的性能提升
  4. 移除distutils
  5. asynchat, asyncore, imp 模块移除
  6. 3.11.4后, 在异常组处理语句内抛出异常时,新异常不再被包装为异常组
  7. slice 对象可哈希所以能作为字典的 key
  8. sum函数的内部改进
  9. 生成器协程不再被 asyncio.iscoroutine 视作协程对象
  10. asyncio.waitasyncio.as_completed 接受生成器协程作为参数
  11. 使用 inspect.markcoroutinefunction 标记同步函数返回协程
  12. shutil.make_archive() 支持 root_dir 参数指定打包根路径
  13. tempfile.mkdtemp() 总是返回绝对路径
  14. typing.TypedDicttyping.NamedTuple的子类支持 __orig_bases__ 属性
  15. typing.dataclass_transform() 添加 frozen_default 参数以控制 frozen 属性
  16. 正则替换相关函数提升了 2-3 倍

总结

  1. PEP 695 新增type语句, 版本升级必需注意
  2. f-strings 优化
  3. venv 创建的虚拟环境中不再预安装setuptools
  4. 垃圾回收在执行循环中的eval breaker时触发, 而不是分配对象时
  5. 支持用TypedDict注解**kwargs
  6. typing.override注解重写
  7. itertools.batched 返回对序列按长度划分为多个元组的迭代器
  8. PEP 684, a unique per-interpreter GIL 每个解释器单独拥有一个 GIL 锁, 提升多核利用能力