What’s New In Python 3.12
Important
venv创建的虚拟环境中不再预安装setuptools- 垃圾回收在执行循环中的
eval breaker时触发, 而不是分配对象时 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
- 使用
TypedDict注解**kwargs
PEP698: typing.override
- 通过
typing.override注解子类重写了父类的该方法 - 如果装饰的函数在父类中不存在, 静态类型检查器会报错
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
asyncio内部的性能提升uuid库支持命令行使用python -m uuid -u uuid4- 因
PEP623, 每个str对象至少减少了 8 个字节 itertools.batched返回对序列按长度划分为多个元组的迭代器- 添加
pathlib.Path.walk(), 行为类似os.walk pathlib.Path.glob()、pathlib.Path.rglob()、pathlib.PurePath.match()添加case_sensitive可选参数以忽略大小写shutil.rmtree()支持 通过onexc回调函数处理删除目录失败的情况tempfile.NamedTemporaryFile支持delete_on_close参数控制在退出上下文时是否删除文件- 增加
threading.settrace_all_threads()和threading.setprofile_all_threads()用于为所有线程设置函数触发回调 - 使用
isinstance(obj, collections.abc.Buffer)检测对象是否支持缓冲区协议
Trivial
PEP 684, a unique per-interpreter GIL每个解释器单独拥有一个 GIL 锁, 提升多核利用能力pathlib.Path支持被继承tokenize的性能提升- 移除
distutils包 asynchat,asyncore,imp模块移除- 在
3.11.4后, 在异常组处理语句内抛出异常时,新异常不再被包装为异常组 slice对象可哈希所以能作为字典的keysum函数的内部改进- 生成器协程不再被
asyncio.iscoroutine视作协程对象 asyncio.wait和asyncio.as_completed接受生成器协程作为参数- 使用
inspect.markcoroutinefunction标记同步函数返回协程 shutil.make_archive()支持root_dir参数指定打包根路径tempfile.mkdtemp()总是返回绝对路径typing.TypedDict和typing.NamedTuple的子类支持__orig_bases__属性typing.dataclass_transform()添加frozen_default参数以控制frozen属性- 正则替换相关函数提升了 2-3 倍
总结
PEP 695新增type语句, 版本升级必需注意f-strings优化venv创建的虚拟环境中不再预安装setuptools- 垃圾回收在执行循环中的
eval breaker时触发, 而不是分配对象时 - 支持用
TypedDict注解**kwargs typing.override注解重写itertools.batched返回对序列按长度划分为多个元组的迭代器PEP 684, a unique per-interpreter GIL每个解释器单独拥有一个 GIL 锁, 提升多核利用能力