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.wait 和 asyncio.as_completed 接受生成器协程作为参数
  11. 使用 inspect.markcoroutinefunction 标记同步函数返回协程
  12. shutil.make_archive() 支持 root_dir 参数指定打包根路径
  13. tempfile.mkdtemp() 总是返回绝对路径
  14. typing.TypedDict 和 typing.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 锁, 提升多核利用能力