Python 3.9 有什么新变化

  • 编者:
  • Łukasz Langa

这篇文章介绍了 Python 3.9 相比 3.8 增加的新特性。 Python 3.9 发布于 2020年 10 月 5 日。 更详细的信息可参阅 更新日志

参见

PEP 596 [https://peps.python.org/pep-0596/] - Python 3.9 发布计划

摘要 — 发布重点

新的语法特性:

新的内置特性:

标准库中的新特性:

解释器的改进:

新的库模块:

发布进程的变化:

请检查代码中的 DeprecationWarning。

Python 2.7 支持未终止时,为了实现向下兼容 Python 2.7,Python 3 保留了许多旧版功能。Python 2 的支持终止后,已经移除了一部分向下兼容层,剩余部分很快也会被移除。这几年,大部分兼容层都会触发 DeprecationWarning 警告。例如,2012 年发布 Python 3.3 后,用 collections.Mapping 替代 collections.abc.Mapping 就会触发 DeprecationWarning

请用 -W default 命令行选项测试应用程序来查看 DeprecationWarningPendingDeprecationWarning,甚至可以用 -W error 将它们视为错误。 可以用 警告过滤器 忽略来自第三方代码的警告。

Python 3.9 是最后一个提供 Python 2 向下兼容层的版本,以给予 Python 项目维护者更多时间移除 Python 2 支持,添加 Python 3.9 支持。

collections 模块中 抽象基类 的别名,例如 collections.abc.Mapping 的别名 collections.Mapping 会为向下兼容最后保留一个发行版。 这些内容将在 Python 3.10 中移除。

更通俗的说法是,请在 Python 开发模式 下运行测试,这样做有助于让代码兼容 Python 的后续版本。

注:一些前期已弃用的内容也将在此 Python 版本中移除。 详见 移除 一节。

新的特性

字典合并与更新运算符

合并 (|) 与更新 (|=) 运算符已被加入内置的 dict 类。 它们为现有的 dict.update{**d1, **d2} 字典合并方法提供了补充。

示例:

  1. >>> x = {"key1": "value1 from x", "key2": "value2 from x"}
  2. >>> y = {"key2": "value2 from y", "key3": "value3 from y"}
  3. >>> x | y
  4. {'key1': 'value1 from x', 'key2': 'value2 from y', 'key3': 'value3 from y'}
  5. >>> y | x
  6. {'key2': 'value2 from x', 'key3': 'value3 from y', 'key1': 'value1 from x'}

详见 PEP 584 [https://peps.python.org/pep-0584/]。(Brandt Bucher 在 bpo-36144 [https://bugs.python.org/issue?@action=redirect&bpo=36144] 中的贡献。)

新增用于移除前缀和后缀的字符串方法

增加了 str.removeprefix(prefix)str.removesuffix(suffix) 用于方便地从字符串移除不需要的前缀或后缀。 也增加了 bytes, bytearray 以及 collections.UserString 的对应方法。 请参阅 PEP 616 [https://peps.python.org/pep-0616/] 了解详情。 (由 Dennis Sweeney 在 bpo-39939 [https://bugs.python.org/issue?@action=redirect&bpo=39939] 中贡献。)

标准多项集中的类型标注泛型

在类型标注中现在你可以使用内置多项集类型例如 listdict 作为通用类型而不必从 typing 导入对应的大写形式类型名 (例如 ListDict)。 标准库中的其他一些类型现在同样也是通用的,例如 queue.Queue

示例:

  1. def greet_all(names: list[str]) -> None:
  2. for name in names:
  3. print("Hello", name)

详见 PEP 585 [https://peps.python.org/pep-0585/]。(由 Guido van Rossum、Ethan Smith 和 Batuhan Taşkaya 在 bpo-39481 [https://bugs.python.org/issue?@action=redirect&bpo=39481] 中贡献。)

新的解析器

Python 3.9 使用于基于 PEG [https://en.wikipedia.org/wiki/Parsing_expression_grammar] 的新解析器替代 LL(1) [https://en.wikipedia.org/wiki/LL_parser]。 新解析器的性能与旧解析器大致相当,但 PEG 在设计新语言特性时的形式化比 LL(1) 更灵活。 我们将在 Python 3.10 及之后版本中开始使用这种灵活性。

ast 模块会使用新解析器并会生成与旧解析器一致的 AST。

在 Python 3.10 中,旧解析器将被删除因而所有依赖它的功能也将被删除(主要是 parser 模块,它早已被弃用)。 只有 在 Python 3.9 中,你可以使用命令行开关 (-X oldparser) 或环境变量 (PYTHONOLDPARSER=1) 切换回 LL(1) 解析器。

请参阅 PEP 617 [https://peps.python.org/pep-0617/] 了解详情。 (由 Guido van Rossum, Pablo Galindo 和 Lysandros Nikolaou 在 bpo-40334 [https://bugs.python.org/issue?@action=redirect&bpo=40334] 中贡献。)

其他语言特性修改

默认设置下,为保证性能,errors 参数只会在第一次发生编码/解码错误时被检查,并且对于空字符串 encoding 参数有时会被忽略。 (由 Victor Stinner 在 bpo-37388 [https://bugs.python.org/issue?@action=redirect&bpo=37388] 中贡献。)

新增模块

zoneinfo

zoneinfo 模块为标准库引入了 IANA 时区数据库。 它添加了 zoneinfo.ZoneInfo,这是一个基于系统时区数据的实体 datetime.tzinfo 实现。

示例:

  1. >>> from zoneinfo import ZoneInfo
  2. >>> from datetime import datetime, timedelta
  3.  
  4. >>> # 夏令时
  5. >>> dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
  6. >>> print(dt)
  7. 2020-10-31 12:00:00-07:00
  8. >>> dt.tzname()
  9. 'PDT'
  10.  
  11. >>> # 标准时
  12. >>> dt += timedelta(days=7)
  13. >>> print(dt)
  14. 2020-11-07 12:00:00-08:00
  15. >>> print(dt.tzname())
  16. PST

作为不附带 IANA 数据库的平台的一个回退数据源,以第一方软件包的形式发布了 tzdata [https://pypi.org/project/tzdata/] 模块 — 通过 PyPI 分发并由 CPython 核心团队维护。

参见

graphlib

添加了新的 graphlib 模块,其中包含 graphlib.TopologicalSorter 类来提供图的拓扑排序功能。 (由 Pablo Galindo, Tim Peters 和 Larry Hastings 在 bpo-17005 [https://bugs.python.org/issue?@action=redirect&bpo=17005] 中贡献。)

改进的模块

ast

indent 选项添加到 dump(),这允许它产生多行缩进的输出。 (由 Serhiy Storchaka 在 bpo-37995 [https://bugs.python.org/issue?@action=redirect&bpo=37995] 中贡献。)

添加了 ast.unparse() 作为 ast 模块中的一个函数,它可被用来反解析 ast.AST 对象并产生相应的代码字符串,当它被解析时将会产生一个等价的 ast.AST 对象。 (由 Pablo Galindo 和 Batuhan Taskaya 在 bpo-38870 [https://bugs.python.org/issue?@action=redirect&bpo=38870] 中贡献。)

为 AST 节点添加了文档字符串,其中包含 ASDL 签名,可被用来构造对应的节点。 (由 Batuhan Taskaya 在 bpo-39638 [https://bugs.python.org/issue?@action=redirect&bpo=39638] 中贡献。)

asyncio

出于重要的安全性考量,asyncio.loop.create_datagram_endpoint()reuse_address 形参不再被支持。 这是由 UDP 中的套接字选项 SO_REUSEADDR 的行为导致的。 更多细节请参阅 loop.create_datagram_endpoint() 的文档。 (由 Kyle Stanley, Antoine Pitrou 和 Yury Selivanov 在 bpo-37228 [https://bugs.python.org/issue?@action=redirect&bpo=37228] 中贡献。。)

添加了新的 coroutine shutdown_default_executor(),它可为等待 ThreadPoolExecutor 结束关闭的默认执行器安排关闭日程操作。 此外,asyncio.run() 已被更新以使用新的 coroutine。 (由 Kyle Stanley 在 bpo-34037 [https://bugs.python.org/issue?@action=redirect&bpo=34037] 中贡献。)

添加了 asyncio.PidfdChildWatcher,这是一个 Linux 专属的子监视器实现,它负责轮询进程的文件描述符。 (bpo-38692 [https://bugs.python.org/issue?@action=redirect&bpo=38692]) 添加了新的 coroutine asyncio.to_thread()。 它主要被用于在单独线程中运行 IO 密集型函数以避免阻塞事件循环,实质上就相当于是 run_in_executor() 的高层级版本,可直接接受关键字参数。 (由 Kyle Stanley 和 Yury Selivanov 在 bpo-32309 [https://bugs.python.org/issue?@action=redirect&bpo=32309] 中贡献。)

当由于超时而取消任务时,asyncio.wait_for() 现在将会等待直到也在 timeout 值 <= 0 的情况下完成取消。 就像 timeout 值为正数时一样。 (由 Elvis Pranskevichus 在 bpo-32751 [https://bugs.python.org/issue?@action=redirect&bpo=32751] 中贡献。)

现在当附带 ssl.SSLSocket 套接字调用不兼容的方法时 asyncio 会引发 TypeError。 (由 Ido Michael 在 bpo-37404 [https://bugs.python.org/issue?@action=redirect&bpo=37404] 中贡献。)

compileall

为重复的 .pyc 文件添加了使用硬软件的可能性: hardlink_dupes 形参以及 —hardlink-dupes 命令行选项。 (由 Lumír 'Frenzy' Balhar 在 bpo-40495 [https://bugs.python.org/issue?@action=redirect&bpo=40495] 中贡献。)

新增了一些用于在结果 .pyc 文件中操纵路径的选项: stripdir, prependdir, limit_sl_dest 形参以及 -s, -p, -e 命令行选项。 并使得为优化等级多次指定选项成为可能。 (由 Lumír 'Frenzy' Balhar 在 bpo-38112 [https://bugs.python.org/issue?@action=redirect&bpo=38112] 中贡献。)

concurrent.futures

将新的 cancel_futures 形参添加到 concurrent.futures.Executor.shutdown(),可以取消尚未开始运行的所有挂起的 Future,而不必等待它们完成运行再关闭执行器。 (由 Kyle Stanley 在 bpo-39349 [https://bugs.python.org/issue?@action=redirect&bpo=39349] 中贡献。)

ThreadPoolExecutorProcessPoolExecutor 中移除了守护线程。 这改善与与子解释器的兼容性及它们在关闭进程时的可预测性。 (由 Kyle Stanley 在 bpo-39812 [https://bugs.python.org/issue?@action=redirect&bpo=39812] 中贡献。)

现在 ProcessPoolExecutor 中的工作进程仅会在没有可重用的空闲工作进程时按需产生。 这优化了启动开销并减少了由空闲工作进程导致的 CPU 时间损失。 (由 Kyle Stanley 在 bpo-39207 [https://bugs.python.org/issue?@action=redirect&bpo=39207] 中贡献。)

curses

增加了 curses.get_escdelay(), curses.set_escdelay(), curses.get_tabsize() 以及 curses.set_tabsize() 函数。(由 Anthony Sottile 在 bpo-38312 [https://bugs.python.org/issue?@action=redirect&bpo=38312] 中贡献。)

datetime

datetime.dateisocalendar() 以及 datetime.datetimeisocalendar() 等方法现在将返回 namedtuple() 而不是 tuple。 (由 Donghee Na 在 bpo-24416 [https://bugs.python.org/issue?@action=redirect&bpo=24416] 中贡献。).)

distutils

upload 命令现在会创建 SHA2-256 和 Blake2b-256 哈希摘要。 它会在禁用 MD5 摘要的平台上跳过 MD5。 (由 Christian Heimes 在 bpo-40698 [https://bugs.python.org/issue?@action=redirect&bpo=40698] 中贡献。)

fcntl

增加了 F_OFD_GETLK, F_OFD_SETLKF_OFD_SETLKW 等常量。 (由 Donghee Na 在 bpo-38602 [https://bugs.python.org/issue?@action=redirect&bpo=38602] 中贡献。)

ftplib

现在 FTPFTP_TLS 当它们的构造器所给定的超时参数为零以防止创建非阻塞套接字时会引发 ValueError。 (由 Donghee Na 在 bpo-39259 [https://bugs.python.org/issue?@action=redirect&bpo=39259] 中贡献。)

gc

当垃圾回收器进行某些复活对象的收集时(在终结器被执行之后这些对象可以在隔离周期之外被访问),不会阻止对所有仍然无法访问的对象的收集。 (由 Pablo Galindo 和 Tim Peters 在 bpo-38379 [https://bugs.python.org/issue?@action=redirect&bpo=38379] 中贡献。)

增加了一个新的函数 gc.is_finalized() 用来检测一个对象是否已被垃圾回收器所终结。 (由 Pablo Galindo 在 bpo-39322 [https://bugs.python.org/issue?@action=redirect&bpo=39322] 中贡献。)

hashlib

hashlib 模块现在会在可能的情况下使用 OpenSSL 中的 SHA3 哈希和 SHAKE XOF。 (由 Christian Heimes 在 bpo-37630 [https://bugs.python.org/issue?@action=redirect&bpo=37630] 中贡献。)

内置的哈希模块现在可通过 ./configure --without-builtin-hashlib-hashes 禁用或通过 ./configure --with-builtin-hashlib-hashes=sha3,blake2 这样的形式有选择地启用以强制使用基于 OpenSSL 的实现。 (由 Christian Heimes 在 bpo-40479 [https://bugs.python.org/issue?@action=redirect&bpo=40479] 中贡献)

http

添加 HTTP 状态码 103 EARLY_HINTS, 418 IM_A_TEAPOT425 TOO_EARLYhttp.HTTPStatus。 (由 Donghee Na 在 bpo-39509 [https://bugs.python.org/issue?@action=redirect&bpo=39509] 以及 Ross Rhodes 在 bpo-39507 [https://bugs.python.org/issue?@action=redirect&bpo=39507] 中贡献。)

IDLE 与 idlelib

添加了切换光标闪烁停止的选项。 (由 Zackery Spytz 在 bpo-4603 [https://bugs.python.org/issue?@action=redirect&bpo=4603] 中贡献。)

Esc 键现在会关闭 IDLE 补全提示窗口。 (由 Johnny Najera 在 bpo-38944 [https://bugs.python.org/issue?@action=redirect&bpo=38944] 中贡献。)

添加关键字到模块名称补全列表。 (由 Terry J. Reedy 在 bpo-37765 [https://bugs.python.org/issue?@action=redirect&bpo=37765] 中贡献。)

3.9 维护版本中的新内容

使 IDLE 调用 sys.excepthook() (当启动时没有 '-n' )。用户钩子以前是被忽略的。 (由 Ken Hilton 在 bpo-43008 [https://bugs.python.org/issue?@action=redirect&bpo=43008] 中贡献。) 上述修改已被反向移植到 3.8 维护发行版中。

重新安排设置对话框。 将常规选项卡分成 Windows 和 Shell/Ed 选项卡。 将扩展帮助菜单的帮助源移至扩展标签。为新选项留出空间,并缩短对话框。后者使对话框更适合小屏幕。 (由 Terry Jan Reedy 贡献于 bpo-40468 [https://bugs.python.org/issue?@action=redirect&bpo=40468] 。) 将缩进空间设置从字体标签移到新的 Windows 标签。 (由 Mark Roseman 和 Terry Jan Reedy 在 bpo-33962 [https://bugs.python.org/issue?@action=redirect&bpo=33962] 中提供)。

.pyi 文件应用语法高亮。 (由 Alex Waygood 和 Terry Jan Reedy 在 bpo-45447 [https://bugs.python.org/issue?@action=redirect&bpo=45447] 中贡献。)

imaplib

现在 IMAP4IMAP4_SSL 的构造器具有可选的 timeout 形参。 并且,现在 open() 方法也具有可选的 timeout 形参包含此修改。 IMAP4_SSLIMAP4_stream 中被重载的方法也应用了此修改。 (由 Donghee Na 在 bpo-38615 [https://bugs.python.org/issue?@action=redirect&bpo=38615] 中贡献。)

增加了 imaplib.IMAP4.unselect()imaplib.IMAP4.unselect() 会释放关联到选定邮箱的服务器资源并将服务器返回到已认证状态。 此命令会执行与 imaplib.IMAP4.close() 相同的动作,区别在于它不会从当前选定邮箱中永久地移除消息。 (由 Donghee Na 在 bpo-40375 [https://bugs.python.org/issue?@action=redirect&bpo=40375] 中贡献。)

importlib

为提升与 import 语句的一致性,现在 importlib.util.resolve_name() 对于无效的相对导入尝试会引发 ImportError 而不是 ValueError。 (由 Ngalim Siregar 在 bpo-37444 [https://bugs.python.org/issue?@action=redirect&bpo=37444] 中贡献。)

发布不可变模块对象的导入加载器除了发布单独模块以外现在也可以发布不可变包。 (由 Dino Viehland 在 bpo-39336 [https://bugs.python.org/issue?@action=redirect&bpo=39336] 中贡献。)

添加了带有对包数据中子目录支持的 importlib.resources.files() 函数,与 importlib_resources 1.5 版的反向端口相匹配。(由 Jason R. Coombs 在 bpo-39791 [https://bugs.python.org/issue?@action=redirect&bpo=39791] 中贡献。)

来自 importlib_metadata 1.6.1 版的已更新 importlib.metadata

inspect

inspect.BoundArguments.arguments 已从 OrderedDict 改为常规字典。 (由 Inada Naoki 在 bpo-36350 [https://bugs.python.org/issue?@action=redirect&bpo=36350] 和 bpo-39775 [https://bugs.python.org/issue?@action=redirect&bpo=39775] 中贡献。)

ipaddress

ipaddress 现在支持 IPv6 作用域地址(即带有 % 前缀的 IPv6 地址)。

IPv6 作用域地址可使用 ipaddress.IPv6Address 来解析。 作用域的区 ID 如果存在,可通过 scope_id 属性来获取。 (由 Oleksandr Pavliuk 在 bpo-34788 [https://bugs.python.org/issue?@action=redirect&bpo=34788] 中贡献。)

从 Python 3.9.5 开始 ipaddress 模块不再接受 IPv4 地址字符串中有任何前缀的零。 (由 Christian Heimes 在 bpo-36384 [https://bugs.python.org/issue?@action=redirect&bpo=36384] 中贡献。)

math

math.gcd() 函数进行了扩展以处理多个参数。 在之前版本中,它只支持两个参数。 (由 Serhiy Storchaka 在 bpo-39648 [https://bugs.python.org/issue?@action=redirect&bpo=39648] 中贡献。)

增加了 math.lcm(): 返回指定参数的最小公倍数。 (由 Mark Dickinson, Ananthakrishnan 和 Serhiy Storchaka 在 bpo-39479 [https://bugs.python.org/issue?@action=redirect&bpo=39479] 和 bpo-39648 [https://bugs.python.org/issue?@action=redirect&bpo=39648] 中贡献。)

增加了 math.nextafter(): 返回从 xy 方向的下一个浮点数值。 (由 Victor Stinner 在 bpo-39288 [https://bugs.python.org/issue?@action=redirect&bpo=39288] 中贡献。)

增加了 math.ulp(): 返回一个浮点数的最小有效比特位。 (由 Victor Stinner 在 bpo-39310 [https://bugs.python.org/issue?@action=redirect&bpo=39310] 中贡献。)

multiprocessing

multiprocessing.SimpleQueue 类新增了 close() 方法用来显式地关闭队列。 (由 Victor Stinner 在 bpo-30966 [https://bugs.python.org/issue?@action=redirect&bpo=30966] 中贡献。)

nntplib

现在 NNTPNNTP_SSL 当它们的构造器所给定的超时值为零以防止创建非阻塞套接字时会引发 ValueError。 (由 Donghee Na 在 bpo-39259 [https://bugs.python.org/issue?@action=redirect&bpo=39259] 中贡献。)

os

增加了 CLD_KILLEDCLD_STOPPED 作为 si_code。 (由 Donghee Na 在 bpo-38493 [https://bugs.python.org/issue?@action=redirect&bpo=38493] 中贡献。)

对外公开了 Linux 专属的 os.pidfd_open() (bpo-38692 [https://bugs.python.org/issue?@action=redirect&bpo=38692]) 和 os.P_PIDFD (bpo-38713 [https://bugs.python.org/issue?@action=redirect&bpo=38713]) 用于文件描述符的进程管理。

现在 os.unsetenv() 函数在 Windows 上也已可用。 (由 Victor Stinner 在 bpo-39413 [https://bugs.python.org/issue?@action=redirect&bpo=39413] 中贡献。)

现在 os.putenv()os.unsetenv() 函数将总是可用。 (由 Victor Stinner 在 bpo-39395 [https://bugs.python.org/issue?@action=redirect&bpo=39395] 中贡献。)

增加了 os.waitstatus_to_exitcode() 函数:将等待状态转换为退出码。 (由 Victor Stinner 在 bpo-40094 [https://bugs.python.org/issue?@action=redirect&bpo=40094] 中贡献。)

pathlib

增加了 pathlib.Path.readlink(),其行为类似于 os.readlink()。 (由 Girts Folkmanis 在 bpo-30618 [https://bugs.python.org/issue?@action=redirect&bpo=30618] 中贡献。)

pdb

在 Windows 上 Pdb 现在支持 ~/.pdbrc。 (由 Tim Hopper 和 Dan Lidral-Porter 在 bpo-20523 [https://bugs.python.org/issue?@action=redirect&bpo=20523] 中贡献。)

poplib

现在 POP3POP3_SSL 当它们的构造器所给定的超时参数为零以防止创建非阻塞套接字时会引发 ValueError。 (由 Donghee Na 在 bpo-39259 [https://bugs.python.org/issue?@action=redirect&bpo=39259] 中贡献。)

pprint

现在 pprint 能美化打印 types.SimpleNamespace。 (由 Carl Bordum Hansen 在 bpo-37376 [https://bugs.python.org/issue?@action=redirect&bpo=37376] 中贡献。)

pydoc

文档字符串的显示现在不仅针对类、函数、方法等,也针对任何具有自己的 __doc__ 属性的对象。 (由 Serhiy Storchaka 在 bpo-40257 [https://bugs.python.org/issue?@action=redirect&bpo=40257] 中贡献。)

random

增加了新的 random.Random.randbytes 方法:生成随机字节串。 (由 Victor Stinner 在 bpo-40286 [https://bugs.python.org/issue?@action=redirect&bpo=40286] 中贡献。)

signal

对外公开了 Linux 专属的 signal.pidfd_send_signal() 用于向使用文件描述符而非 pid 的进程发送信号。 (bpo-38712 [https://bugs.python.org/issue?@action=redirect&bpo=38712])

smtplib

现在 SMTPSMTP_SSL 当它们的构造器所给定的超时参数为零以防止创建非阻塞套接字时会引发 ValueError。 (由 Donghee Na 在 bpo-39259 [https://bugs.python.org/issue?@action=redirect&bpo=39259] 中贡献。)

现在 LMTP 构造器具有一个可选的 timeout 形参。 (由 Donghee Na 在 bpo-39329 [https://bugs.python.org/issue?@action=redirect&bpo=39329] 中贡献。)

socket

现在 socket 模块会在 Linux 4.1 或更高版本上导出 CAN_RAW_JOIN_FILTERS 常量。 (由 Stefan Tatschner 和 Zackery Spytz 在 bpo-25780 [https://bugs.python.org/issue?@action=redirect&bpo=25780] 中贡献。)

现在 socket 模块会在受到支持的平台上支持 CAN_J1939 协议。 (由 Karl Ding 在 bpo-40291 [https://bugs.python.org/issue?@action=redirect&bpo=40291] 中贡献。)

现在 socket 模块具有 socket.send_fds()socket.recv_fds() 函数。 (由 Joannah Nanjekye, Shinya Okano 和 Victor Stinner 在 bpo-28724 [https://bugs.python.org/issue?@action=redirect&bpo=28724] 中贡献。)

time

在 AIX 上,现在 thread_time() 是使用具有纳秒级精度的 thread_cputime() 实现,而不再是只有 10 毫秒精度的 clock_gettime(CLOCK_THREAD_CPUTIME_ID)。 (由 Batuhan Taskaya 在 bpo-40192 [https://bugs.python.org/issue?@action=redirect&bpo=40192] 中贡献)

sys

新增 sys.platlibdir 属性:平台专属库目录的名称。 它被用于构建标准库的路径和已安装扩展模块的路径。 它在大多数平台上等于 "lib"。 在 Fedora 和 SuSE 上,对于 64 位平台它将等于 "lib64"。 (由 Jan Matějek, Matěj Cepl, Charalampos Stratakis 和 Victor Stinner 在 bpo-1294959 [https://bugs.python.org/issue?@action=redirect&bpo=1294959] 中贡献。)

在之前版本中,sys.stderr 在非交互模式时是带块缓冲的。 现在 stderr 默认总是带行缓冲的。 (由 Jendrik Seipp 在 bpo-13601 [https://bugs.python.org/issue?@action=redirect&bpo=13601] 中贡献。)

tracemalloc

增加了 tracemalloc.reset_peak() 用于将跟踪的内存块峰值大小设为当前大小,以测量特定代码段的峰值。 (由 Huon Wilson 在 bpo-40630 [https://bugs.python.org/issue?@action=redirect&bpo=40630] 中贡献。)

typing

PEP 593 [https://peps.python.org/pep-0593/] 引入了一种 typing.Annotated 类型以使用上下文专属的元数据来装饰现有类型,并将新的 include_extras 形参添加到 typing.get_type_hints() 以在运行时访问元数据。 (由 Till Varoquaux 和 Konstantin Kashin 贡献。)

unicodedata

Unicode 数据库已更新到 13.0.0 版。 (bpo-39926 [https://bugs.python.org/issue?@action=redirect&bpo=39926])。

venv

venv 所提供的激活脚本现在总是会使用 __VENV_PROMPT__ 设置的值来一致地指明它们的自定义提示符。 在之前版本中某些脚本会无条件地使用 __VENV_PROMPT__,而另一些脚本只在其恰好被设置时(这是默认情况)才会使用,还有的脚本会改用 __VENV_NAME__。 (由 Brett Cannon 在 bpo-37663 [https://bugs.python.org/issue?@action=redirect&bpo=37663] 中贡献。)

xml

当把 xml.etree.ElementTree 序列化为 XML 文件时属性内部的空白字符现在将被保留。 不同的行结束符不会再被正规化为 "n"。 这是对于如何解读 XML 规范 2.11 节的相关讨论的最终结果。 (由 Mefistotelis 在 bpo-39011 [https://bugs.python.org/issue?@action=redirect&bpo=39011] 中贡献。)

性能优化

  • 优化了在推导式中为临时变量赋值的惯用方式。 现在推导式中的 for y in [expr] 会与简单赋值语句 y = expr 一样快速。 例如:

sums = [s for s in [0] for x in data for s in [s + x]]

不同于 := 运算符,这个惯用方式不会使变量泄露到外部作用域中。

(由 Serhiy Storchaka 在 bpo-32856 [https://bugs.python.org/issue?@action=redirect&bpo=32856] 中贡献。)

  • 优化了多线程应用中的信号处理。 如果一个线程不是获得信号的主线程,字节码求值循环不会在每条字节码指令上被打断以检查无法被处理的挂起信号。 只有主解释器的主线程能够处理信号。

在之前版本中,字节码求值循环会在每条指令上被打断直到主线程处理了信号。 (由 Victor Stinner 在 bpo-40010 [https://bugs.python.org/issue?@action=redirect&bpo=40010] 上贡献。)

以下是对从 Python 3.4 到 Python 3.9 的提升提升情况的总结:

  1. Python version 3.4 3.5 3.6 3.7 3.8 3.9
  2. -------------- --- --- --- --- --- ---
  3.  
  4. Variable and attribute read access:
  5. read_local 7.1 7.1 5.4 5.1 3.9 3.9
  6. read_nonlocal 7.1 8.1 5.8 5.4 4.4 4.5
  7. read_global 15.5 19.0 14.3 13.6 7.6 7.8
  8. read_builtin 21.1 21.6 18.5 19.0 7.5 7.8
  9. read_classvar_from_class 25.6 26.5 20.7 19.5 18.4 17.9
  10. read_classvar_from_instance 22.8 23.5 18.8 17.1 16.4 16.9
  11. read_instancevar 32.4 33.1 28.0 26.3 25.4 25.3
  12. read_instancevar_slots 27.8 31.3 20.8 20.8 20.2 20.5
  13. read_namedtuple 73.8 57.5 45.0 46.8 18.4 18.7
  14. read_boundmethod 37.6 37.9 29.6 26.9 27.7 41.1
  15.  
  16. Variable and attribute write access:
  17. write_local 8.7 9.3 5.5 5.3 4.3 4.3
  18. write_nonlocal 10.5 11.1 5.6 5.5 4.7 4.8
  19. write_global 19.7 21.2 18.0 18.0 15.8 16.7
  20. write_classvar 92.9 96.0 104.6 102.1 39.2 39.8
  21. write_instancevar 44.6 45.8 40.0 38.9 35.5 37.4
  22. write_instancevar_slots 35.6 36.1 27.3 26.6 25.7 25.8
  23.  
  24. Data structure read access:
  25. read_list 24.2 24.5 20.8 20.8 19.0 19.5
  26. read_deque 24.7 25.5 20.2 20.6 19.8 20.2
  27. read_dict 24.3 25.7 22.3 23.0 21.0 22.4
  28. read_strdict 22.6 24.3 19.5 21.2 18.9 21.5
  29.  
  30. Data structure write access:
  31. write_list 27.1 28.5 22.5 21.6 20.0 20.0
  32. write_deque 28.7 30.1 22.7 21.8 23.5 21.7
  33. write_dict 31.4 33.3 29.3 29.2 24.7 25.4
  34. write_strdict 28.4 29.9 27.5 25.2 23.1 24.5
  35.  
  36. Stack (or queue) operations:
  37. list_append_pop 93.4 112.7 75.4 74.2 50.8 50.6
  38. deque_append_pop 43.5 57.0 49.4 49.2 42.5 44.2
  39. deque_append_popleft 43.7 57.3 49.7 49.7 42.8 46.4
  40.  
  41. Timing loop:
  42. loop_overhead 0.5 0.6 0.4 0.3 0.3 0.3

以上结果是由以下变量访问基准测试脚本所生成的: Tools/scripts/var_access_benchmark.py。 该基准测试脚本以纳秒为单位显示时间。 基准测试数据是在一块 Intel® Core™ i7-4960HQ 处理器 [https://ark.intel.com/content/www/us/en/ark/products/76088/intel-core-i7-4960hq-processor-6m-cache-up-to-3-80-ghz.html] 运行从 python.org [https://www.python.org/downloads/macos/] 获取的 macOS 64 位编译版本所得到的。

弃用

(由 Victor Stinner 在 bpo-39353 [https://bugs.python.org/issue?@action=redirect&bpo=39353] 中贡献。)

移除

移植到 Python 3.9

本节列出了先前描述的更改以及可能需要更改代码的其他错误修正.

Python API 的变化

C API 的变化

  • Instances of 堆分配类型 的实例(例如使用 PyType_FromSpec() 和类似 API 创建的实例)自 Python 3.8 起会带有一个对其类型对象的引用。 正如 Python 3.8 的 "C API 的变化" 部分所述,对于大多数情况来说,这应当不会有任何副作用,但对于具有自定义 tp_traverse 函数的类型来说,则要确保所有堆分配类型的自定义 tp_traverse 函数可访问对象的类型。

示例:

  1. int
  2. foo_traverse(foo_struct self, visitproc visit, void arg) {
  3. // 遍历函数的其余部分
  4. #if PY_VERSION_HEX >= 0x03090000 // 这在 Python 3.9 之前不需要 (Python 问题报告 35810 和 40217) Py_VISIT(Py_TYPE(self));
  5. #endif
  6. }

如果你的遍历函数委托给了其基类(或其他类)的 tp_traverse,则要确保 Py_TYPE(self) 只被访问一次。 请注意应当只有 堆类型 可访问 tp_traverse 中的类型。

举例来说,如果你的 tp_traverse 函数包括以下内容:

  1. base->tp_traverse(self, visit, arg)

则要添加:

  1. #if PY_VERSION_HEX >= 0x03090000 // 这在 Python 3.9 之前是不需要的 (bpo-35810 和 bpo-40217) if (base->tp_flags & Py_TPFLAGS_HEAPTYPE) { // 一个堆类型的 tp_traverse 已经访问了 Py_TYPE(self) } else { Py_VISIT(Py_TYPE(self)); }
  2. #else

(参阅 bpo-35810 [https://bugs.python.org/issue?@action=redirect&bpo=35810] 和 bpo-40217 [https://bugs.python.org/issue?@action=redirect&bpo=40217] 了解更多信息。)

CPython 字节码的改变

(由 Mark Shannon 在 bpo-39156 [https://bugs.python.org/issue?@action=redirect&bpo=39156] 中贡献。)

构建的改变

C API 的改变

新的特性

(由 Victor Stinner 在 bpo-38644 [https://bugs.python.org/issue?@action=redirect&bpo=38644] 和 bpo-39542 [https://bugs.python.org/issue?@action=redirect&bpo=39542] 中贡献。)

移植到 Python 3.9

没有模块状态的扩展模块 (m_size <= 0) 不会受到影响。

(详情请参阅 bpo-40170 [https://bugs.python.org/issue?@action=redirect&bpo=40170]。)

移除

  • pyfpe.hPyFPE_START_PROTECT()PyFPE_END_PROTECT() 宏已从受限的 C API 中被排除。 (由 Victor Stinner 在 bpo-38835 [https://bugs.python.org/issue?@action=redirect&bpo=38835] 中贡献。)

  • PyTypeObjecttp_print 空位已被移除。 它在 Python 2.7 及之前的版本中被用来将对象打印到文件。 自 Python 3.0 起,它已被忽略并且不再使用。 (由 Jeroen Demeyer 在 bpo-36974 [https://bugs.python.org/issue?@action=redirect&bpo=36974] 中贡献。)

  • 受限 C API 中的改变(如果定义了 Py_LIMITED_API 宏):

    • 以下函数已从受限 C API 中排除:

      • PyThreadState_DeleteCurrent() (由 Joannah Nanjekye 在 bpo-37878 [https://bugs.python.org/issue?@action=redirect&bpo=37878] 中贡献。)

      • PyCheckRecursionLimit

      • PyNewReference()

      • PyForgetReference()

      • PyTraceMallocNewReference()

      • PyGetRefTotal()

      • 在受限 C API 中从未使用的垃圾箱机制。

      • PyTrash_UNWIND_LEVEL

      • Py_TRASHCAN_BEGIN_CONDITION

      • Py_TRASHCAN_BEGIN

      • Py_TRASHCAN_END

      • Py_TRASHCAN_SAFE_BEGIN

      • Py_TRASHCAN_SAFE_END

    • 已将下列函数和定义移至内部 C API:

      • PyDebugPrintTotalRefs()

      • PyPrintReferences()

      • PyPrintReferenceAddresses()

      • Pytracemalloc_config

      • PyAddToAllObjects() (Py_TRACE_REFS 构建专属)

(由 Victor Stinner 在 bpo-38644 [https://bugs.python.org/issue?@action=redirect&bpo=38644] 和 bpo-39542 [https://bugs.python.org/issue?@action=redirect&bpo=39542] 中贡献。)

Python 3.9.1 中的重要变化

typing

typing.Literal 的行为被改为遵循 PEP 586 [https://peps.python.org/pep-0586/] 并匹配该 PEP 所描述的静态类型检查器的行为。

  • Literal 现在将是去重复的形参。

  • Literal 对象间的相等性比较现在将是顺序无关的。

  • Literal 比较现在会考虑类型。 例如 Literal[0] == Literal[False] 之前的结果值为 True。 现在则为 False。 为支持此改变,内部使用的类型缓存现在也支持区分类型。

  • 现在如果有任何一个参数不为 hashableLiteral 对象将在相等性比较期间引发 TypeError。 请注意使用可变参数声明 Literal 将不会抛出异常:

  1. >>> from typing import Literal
  2. >>> Literal[{0}]
  3. >>> Literal[{0}] == Literal[{False}]
  4. Traceback (most recent call last):
  5. File "<stdin>", line 1, in <module>
  6. TypeError: unhashable type: 'set'

(由 Yurii Karabas 在 bpo-42345 [https://bugs.python.org/issue?@action=redirect&bpo=42345] 中贡献。)

macOS 11.0 (Big Sur) 与 Apple Silicon Mac 支持

对于 3.9.1 版来说,Python 现在完全支持在 macOS 11.0 (Big Sur) 和 Apple Silicon Macs (基于 ARM64 架构) 上构建和运行。 现在提供了一个新的通用构建类型 universal2,用于在一组可执行文件上原生支持 ARM64Intel 64。 二进制文件现在也可以在当前版本的 macOS 上编译以部署到多种较旧的 macOS 版本上 (已测试 10.9),同时会基于运行时所使用的操作系统版本让某些较新的 OS 功能和选项有条件地可用 ("弱链接") 。

(由 Ronald Oussoren 和 Lawrence D'Anna 在 bpo-41100 [https://bugs.python.org/issue?@action=redirect&bpo=41100] 中贡献。)

Python 3.9.2 中的重要变化

collections.abc

现在 collections.abc.Callable 泛型会将类型形参展平,类似于 typing.Callable 当前所做的那样。 这意味着 collections.abc.Callable[[int, str], str]__args__ 将为 (int, str, str);之前则为 ([int, str], str)。 为了允许这个改变,types.GenericAlias 现在可以被子类化,并且在抽取 collections.abc.Callable 类型时将返回一个子类。 通过 typing.get_args()__args__ 访问参数的代码需要考虑到这个改变。 对于无效的 collections.abc.Callable 参数化形式可能会发出 DeprecationWarning,这在 Python 3.9.1 中可能会静默地传递。 这个 DeprecationWarning 将在 Python 3.10 中变为 TypeError。 (由 Ken Jin 在 bpo-42195 [https://bugs.python.org/issue?@action=redirect&bpo=42195] 中贡献。)

urllib.parse

较早的 Python 版本允许同时使用 ;& 作为 urllib.parse.parse_qs()urllib.parse.parse_qsl() 中查询形参的分隔符。 出于安全考虑,并遵循新版的 W3C 建议,这已被更改为只允许一种分隔符,默认为 &。 这一改变也会影响 cgi.parse()cgi.parse_multipart() 因为它们在内部使用了受影响的函数。 要了解更多细节,请参阅相应的文档。 (由 Adam Goldschmidt, Senthil Kumaran 和 Ken Jin 在 bpo-42967 [https://bugs.python.org/issue?@action=redirect&bpo=42967] 中贡献。)

Python 3.9.3 中的重要变化

新的安全修正将 ftplib.FTP 的行为改成当设置被动数据通道时不信任远程服务器所发送的 IPv4 地址。 我们会改为重用 ftp 服务器的 IP 地址。 对于需要原先的行为的不常见代码,请在你的 FTP 实例上将 trust_server_pasv_ipv4_address 属性设为 True。 (参见 gh-87451 [https://github.com/python/cpython/issues/87451]。)

Python 3.9.5 中的重要变化

urllib.parse

在 URL 中存在换行符或制表符可能会导致某种形式的攻击。 根据更新了 RFC 3986 [https://datatracker.ietf.org/doc/html/rfc3986.html] 的 WHATWG 规范,urllib.parse 中的解析器将从 URL 中去除 ASCII 换行符 \n, \r 和制表符 \t 以防止这种攻击。 移除的字符将由一个新的模块层级变量 urllib.parse._UNSAFE_URL_BYTES_TO_REMOVE 来控制。 (参见 gh-88048 [https://github.com/python/cpython/issues/88048]。)

3.9.14 中的重要安全特性

使用 2 (二进制), 4, 8 (八进制), 16 (十六进制) 或 32 以外的基数例如以 10 (十进制) 为基数在 intstr 之间进行转换现在如果字符串表示形式中的位数超过特定限制则会引发 ValueError 以避免因算法复杂度导致的拒绝服务攻击风险。 这是对于 CVE 2020-10735 [https://www.cve.org/CVERecord?id=CVE-2020-10735] 的一种缓解方案。 此限制可通过环境变量、命令行旗标或 sys API 来配置或者禁用。 参见 整数字符串转换长度限制 文档。 字符串形式的默认限制为 4300 位数字。

3.9.17 中的重要变化

tarfile