Python 3.8 有什么新变化

  • 编者:
  • Raymond Hettinger(译者:wh2099 at outlook dot com)

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

摘要 — 发布重点

新的特性

赋值表达式

新增的语法 := 可在表达式内部为变量赋值。 它被昵称为“海象运算符”因为它很像是 海象的眼睛和长牙.jpg) [https://en.wikipedia.org/wiki/Walrus#/media/File:Pacific_Walrus_-_Bull_(8247646168).jpg]。

在这个示例中,赋值表达式可以避免调用 len() 两次:

  1. if (n := len(a)) > 10:
  2. print(f"List is too long ({n} elements, expected <= 10)")

类似的益处还可出现在正则表达式匹配中需要使用两次匹配对象的情况中,一次检测用于匹配是否发生,另一次用于提取子分组:

  1. discount = 0.0
  2. if (mo := re.search(r'(\d+)% discount', advertisement)):
  3. discount = float(mo.group(1)) / 100.0

此运算符也适用于配合 while 循环计算一个值来检测循环是否终止,而同一个值又在循环体中再次被使用的情况:

  1. # 循环处理固定长度的数据块
  2. while (block := f.read(256)) != '':
  3. process(block)

另一个值得介绍的用例出现于列表推导式中,在筛选条件中计算一个值,而同一个值又在表达式中需要被使用:

  1. [clean_name.title() for name in names
  2. if (clean_name := normalize('NFC', name)) in allowed_names]

请尽量将海象运算符的使用限制在清晰的场合中,以降低复杂性并提升可读性。

请参阅 PEP 572 [https://peps.python.org/pep-0572/] 了解详情。

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

仅限位置形参

新增了一个函数形参语法 / 用来指明某些函数形参必须作为位置参数而不可作为关键字参数。 这种标记语法与通过 help() 显示的使用 Larry Hastings 的 Argument Clinic [https://devguide.python.org/development-tools/clinic/] 工具所标记的 C 函数相同。

在下面的例子中,形参 ab 为仅限位置形参,cd 可以是位置形参或关键字形参,而 ef 要求为关键字形参:

  1. def f(a, b, /, c, d, *, e, f):
  2. print(a, b, c, d, e, f)

以下均为合法的调用:

  1. f(10, 20, 30, d=40, e=50, f=60)

但是,以下均为不合法的调用:

  1. f(10, b=20, c=30, d=40, e=50, f=60) # b 不能是关键字参数
  2. f(10, 20, 30, 40, 50, f=60) # e 必须是关键字参数

这种标记形式的一个用例是它允许纯 Python 函数完整模拟现有的用 C 代码编写的函数的行为。 例如,内置的 divmod() 函数不接受关键字参数:

  1. def divmod(a, b, /):
  2. "Emulate the built in divmod() function"
  3. return (a // b, a % b)

另一个用例是在不需要形参名称时排除关键字参数。 例如,内置的 len() 函数的签名为 len(obj, /)。 这可以排除如下这种笨拙的调用形式:

  1. len(obj='hello') # "obj" 关键字参数损害了可读性

另一个益处是将形参标记为仅限位置形参将允许在未来修改形参名而不会破坏客户的代码。 例如,在 statistics 模块中,形参名 dist 在未来可能被修改。 这使得以下函数描述成为可能:

  1. def quantiles(dist, /, *, n=4, method='exclusive')
  2. ...

由于在 / 左侧的形参不会被公开为可用关键字,其他形参名仍可在 **kwargs 中使用:

  1. >>> def f(a, b, /, **kwargs):
  2. ... print(a, b, kwargs)
  3. ...
  4. >>> f(10, 20, a=1, b=2, c=3) # a 和 b 以两种方式被使用
  5. 10 20 {'a': 1, 'b': 2, 'c': 3}

这极大地简化了需要接受任意关键字参数的函数和方法的实现。 例如,以下是一段摘自 collections 模块的代码:

  1. class Counter(dict):
  2.  
  3. def __init__(self, iterable=None, /, **kwds):
  4. # 请注意 "iterable" 可以是关键字参数

请参阅 PEP 570 [https://peps.python.org/pep-0570/] 了解详情。

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

用于已编译字节码文件的并行文件系统缓存

新增的 PYTHONPYCACHEPREFIX 设置 (也可使用 -X pycache_prefix) 可将隐式的字节码缓存配置为使用单独的并行文件系统树,而不是默认的每个源代码目录下的 __pycache__ 子目录。

缓存的位置会在 sys.pycache_prefix 中报告 (None 表示默认位置即 __pycache__ 子目录)。

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

调试构建使用与发布构建相同的 ABI

Python 现在不论是以发布模式还是调试模式进行构建都将使用相同的 ABI。 在 Unix 上,当 Python 以调试模式构建时,现在将可以加载以发布模式构建的 C 扩展和使用稳定版 ABI 构建的 C 扩展。

发布编译版和 调试编译版 现在都是 ABI 兼容的:定义 Py_DEBUG 宏不会再应用 Py_TRACE_REFS 宏,它引入 了唯一的 ABI 不兼容性。 Py_TRACE_REFS 宏添加了 sys.getobjects() 函数和 PYTHONDUMPREFS 环境变量,它可以使用新的 ./configure --with-trace-refs 编译选项来设置。 (由 Victor Stinner 在 bpo-36465 [https://bugs.python.org/issue?@action=redirect&bpo=36465] 中贡献。)

在 Unix 上,C 扩展不会再被链接到 libpython,但 Android 和 Cygwin 例外。 现在静态链接的 Python 将可以加载使用共享库 Python 构建的 C 扩展。 (由 Victor Stinner 在 bpo-21536 [https://bugs.python.org/issue?@action=redirect&bpo=21536] 中贡献。)

在 Unix 上,当 Python 以调试模式构建时,导入操作现在也会查找在发布模式下编译的 C 扩展以及使用稳定版 ABI 编译的 C 扩展。 (由 Victor Stinner 在 bpo-36722 [https://bugs.python.org/issue?@action=redirect&bpo=36722] 中贡献。)

要将 Python 嵌入到一个应用中,必须将新增的 --embed 选项传给 python3-config --libs --embed 以获得 -lpython3.8 (将应用链接到 libpython)。 要同时支持 3.8 和旧版本,请先尝试 python3-config --libs --embed 并在此命令失败时回退到 python3-config --libs (即不带 --embed)。

增加一个 pkg-config python3.8-embed 模块用来将 Python 嵌入到一个应用中: pkg-config python3.8-embed --libs 包含 -lpython3.8。 要同时支持 3.8 和旧版本,请先尝试 pkg-config pythonX.Y-embed --libs 并在此命令失败时回退到 pkg-config pythonX.Y --libs (即不带 --embed) (请将 X.Y 替换为 Python 版本号)。

另一方面,pkg-config python3.8 --libs 不再包含 -lpython3.8。 C 扩展不可被链接到 libpython (但 Android 和 Cygwin 例外,这两者的情况由脚本处理);此改变是故意被设为向下不兼容的。 (由 Victor Stinner 在 bpo-36721 [https://bugs.python.org/issue?@action=redirect&bpo=36721] 中贡献。)

f-字符串支持 = 用于自动记录表达式和调试文档

增加 = 说明符用于 f-string。 形式为 f'{expr=}' 的 f-字符串将扩展表示为表达式文本,加一个等于号,再加表达式的求值结果。 例如:

  1. >>> user = 'eric_idle'
  2. >>> member_since = date(1975, 7, 31)
  3. >>> f'{user=} {member_since=}'
  4. "user='eric_idle' member_since=datetime.date(1975, 7, 31)"

通常的 f-字符串格式说明符 允许更细致地控制所要显示的表达式结果:

  1. >>> delta = date.today() - member_since
  2. >>> f'{user=!s} {delta.days=:,d}'
  3. 'user=eric_idle delta.days=16,075'

= 说明符将输出整个表达式,以便详细演示计算过程:

  1. >>> print(f'{theta=} {cos(radians(theta))=:.3f}')
  2. theta=30 cos(radians(theta))=0.866

(由 Eric V. Smith 和 Larry Hastings 在 bpo-36817 [https://bugs.python.org/issue?@action=redirect&bpo=36817] 中贡献。)

PEP 578: Python 运行时审核钩子

此 PEP 添加了审核钩子和已验证开放钩子。 两者在 Python 与本机代码中均可用。允许以纯 Python 代码编写的应用和框架利用额外的通知,同时允许嵌入开发人员或系统管理员部署始终启用审核的 Python 版本。

请参阅 PEP 578 [https://peps.python.org/pep-0578/] 了解详情。

PEP 587: Python 初始化配置

PEP 587 [https://peps.python.org/pep-0587/] 增加了一个新的 C API 用来配置 Python 初始化,提供对整个配置过程的更细致控制以及更好的错误报告。

新的结构:

新的函数:

此 PEP 还为这些内部结构添加了 _PyRuntimeState.preconfig (PyPreConfig 类型) 和 PyInterpreterState.config (PyConfig 类型) 字段。 PyInterpreterState.config 成为新的引用配置,替代全局配置变量和其他私有变量。

请参阅 Python 初始化配置 获取详细文档。

请参阅 PEP 587 [https://peps.python.org/pep-0587/] 了解详情。

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

PEP 590: Vectorcall: 用于 CPython 的快速调用协议

Vectorcall 协议 添加到 Python/C API。 它的目标是对已被应用于多个类的现有优先进行正式化。 任何实现了可调用对象的 静态类型 均可使用此协议。

此特性目前为暂定状态,计划在 Python 3.9 将其完全公开。

请参阅 PEP 590 [https://peps.python.org/pep-0590/] 了解详情。

(由 Jeroen Demeyer, Mark Shannon 和 Petr Viktorin 在 bpo-36974 [https://bugs.python.org/issue?@action=redirect&bpo=36974] 中贡献。)

具有外部数据缓冲区的 pickle 协议 5

当使用 pickle 在 Python 进程间传输大量数据以充分发挥多核或多机处理的优势时,非常重要一点是通过减少内存拷贝来优化传输效率,并可能应用一些定制技巧例如针对特定数据的压缩。

pickle 协议 5 引入了对于外部缓冲区的支持,这样 PEP 3118 [https://peps.python.org/pep-3118/] 兼容的数据可以与主 pickle 流分开进行传输,这是由通信层来确定的。

请参阅 PEP 574 [https://peps.python.org/pep-0574/] 了解详情。

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

其他语言特性修改

  1. >>> notice = 'Copyright © 2019'
  2. >>> copyright_year_pattern = re.compile(r'\N{copyright sign}\s*(\d{4})')
  3. >>> int(copyright_year_pattern.search(notice).group(1))
  4. 2019

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

  1. >>> def parse(family):
  2. lastname, *members = family.split()
  3. return lastname.upper(), *members
  4.  
  5. >>> parse('simpsons homer marge bart lisa maggie')
  6. ('SIMPSONS', 'homer', 'marge', 'bart', 'lisa', 'maggie')

(由 David Cuthbert 和 Jordan Chapman 在 bpo-32117 [https://bugs.python.org/issue?@action=redirect&bpo=32117] 中贡献。)

下面是一个修改 statistics.mean() 函数来防止 data 形参被用作关键字参数的例子:

  1. >>> from statistics import mean
  2. >>> mean(data=[10, 20, 90])
  3. 40
  4. >>> mean.__code__ = mean.__code__.replace(co_posonlyargcount=1)
  5. >>> mean(data=[10, 20, 90])
  6. Traceback (most recent call last): ...
  7. TypeError: mean() got some positional-only arguments passed as keyword arguments: 'data'

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

  1. >>> pow(38, -1, 137)
  2. 119
  3. >>> 119 * 38 % 137
  4. 1

模乘逆元在求解 线性丢番图方程 [https://en.wikipedia.org/wiki/Diophantine_equation] 会被用到。 例如,想要求出 4258𝑥 + 147𝑦 = 369 的整数解,首先应重写为 4258𝑥 ≡ 369 (mod 147) 然后求解:

  1. >>> x = 369 * pow(4258, -1, 147) % 147
  2. >>> y = (4258 * x - 369) // -147
  3. >>> 4258 * x + 147 * y
  4. 369

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

  • 字典推导式已与字典字面值实现同步,会先计算键再计算值:
  1. >>> # 字典推导式
  2. >>> cast = {input('role? '): input('actor? ') for i in range(2)}
  3. role? King Arthur
  4. actor? Chapman
  5. role? Black Knight
  6. actor? Cleese
  7.  
  8. >>> # 字典字面值
  9. >>> cast = {input('role? '): input('actor? ')}
  10. role? Sir Robin
  11. actor? Eric Idle

对执行顺序的保证对赋值表达式来说很有用,因为在键表达式中赋值的变量将可在值表达式中被使用:

  1. >>> names = ['Martin von Löwis', 'Łukasz Langa', 'Walter Dörwald']
  2. >>> {(n := normalize('NFC', name)).casefold() : n for name in names}
  3. {'martin von löwis': 'Martin von Löwis',
  4. 'łukasz langa': 'Łukasz Langa',
  5. 'walter dörwald': 'Walter Dörwald'}

(由 Jörn Heissler 在 bpo-35224 [https://bugs.python.org/issue?@action=redirect&bpo=35224] 中贡献。)

  • object.__reduce__() 方法现在可返回长度为二至六个元素的元组。 之前的上限为五个。 新增的第六个可选元素是签名为 (obj, state) 的可调用对象。 这样就允许直接控制特定对象的状态更新。 如果元素值不为 None,该可调用对象将优先于对象的 __setstate__() 方法。 (由 Pierre Glaser 和 Olivier Grisel 在 bpo-35900 [https://bugs.python.org/issue?@action=redirect&bpo=35900] 中贡献。)

新增模块

  • 新增的 importlib.metadata 模块提供了从第三方包读取元数据的(临时)支持。 例如,它可以提取一个已安装软件包的版本号、入口点列表等等:
  1. >>> # 请注意以下示例要求常用的 "requests" 包
  2. >>> # 已经被安装
  3. >>>
  4. >>> from importlib.metadata import version, requires, files
  5. >>> version('requests')
  6. '2.22.0'
  7. >>> list(requires('requests'))
  8. ['chardet (<3.1.0,>=3.0.2)']
  9. >>> list(files('requests'))[:5]
  10. [PackagePath('requests-2.22.0.dist-info/INSTALLER'),
  11. PackagePath('requests-2.22.0.dist-info/LICENSE'),
  12. PackagePath('requests-2.22.0.dist-info/METADATA'),
  13. PackagePath('requests-2.22.0.dist-info/RECORD'),
  14. PackagePath('requests-2.22.0.dist-info/WHEEL')]

(由 Barry Warsaw 和 Jason R. Coombs 在 bpo-34632 [https://bugs.python.org/issue?@action=redirect&bpo=34632] 中贡献)。

改进的模块

ast

AST 节点现在具有 end_linenoend_col_offset 属性,它们给出节点结束的精确位置。 (这只适用于具有 linenocol_offset 属性的节点。)

新增函数 ast.get_source_segment() 返回指定 AST 节点的源代码。

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

ast.parse() 函数具有一些新的旗标:

(由 Guido van Rossum 在 bpo-35766 [https://bugs.python.org/issue?@action=redirect&bpo=35766] 中贡献。)

asyncio

asyncio.run() 已经从暂定状态晋级为稳定 API。 此函数可被用于执行一个 coroutine 并返回结果,同时自动管理事件循环。 例如:

  1. import asyncio
  2.  
  3. async def main():
  4. await asyncio.sleep(0)
  5. return 42
  6.  
  7. asyncio.run(main())

大致 等价于:

  1. import asyncio
  2.  
  3. async def main():
  4. await asyncio.sleep(0)
  5. return 42
  6.  
  7. loop = asyncio.new_event_loop()
  8. asyncio.set_event_loop(loop)
  9. try:
  10. loop.run_until_complete(main())
  11. finally:
  12. asyncio.set_event_loop(None)
  13. loop.close()

实际的实现要更复杂许多。 因此 asyncio.run() 应该作为运行 asyncio 程序的首选方式。

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

运行 python -m asyncio 将启动一个原生异步 REPL。 这允许快速体验具有最高层级 await 的代码。 这时不再需要直接调用 asyncio.run(),因为此操作会在每次唤起时产生一个新事件循环:

  1. $ python -m asyncio
  2. asyncio REPL 3.8.0
  3. Use "await" directly instead of "asyncio.run()".
  4. Type "help", "copyright", "credits" or "license" for more information.
  5. >>> import asyncio
  6. >>> await asyncio.sleep(10, result='hello')
  7. hello

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

异常 asyncio.CancelledError 现在继承自 BaseException 而不是 Exception 并且不再继承自 concurrent.futures.CancelledError。 (由 Yury Selivanov 在 bpo-32528 [https://bugs.python.org/issue?@action=redirect&bpo=32528] 中贡献。)

在 Windows 上,现在默认的事件循环为 ProactorEventLoop。 (由 Victor Stinner 在 bpo-34687 [https://bugs.python.org/issue?@action=redirect&bpo=34687] 中贡献。)

ProactorEventLoop 现在也支持 UDP。 (由 Adam Meily 和 Andrew Svetlov 在 bpo-29883 [https://bugs.python.org/issue?@action=redirect&bpo=29883] 中贡献。)

ProactorEventLoop 现在可通过 KeyboardInterrupt ("CTRL+C") 来中断。 (由 Vladimir Matveev 在 bpo-23057 [https://bugs.python.org/issue?@action=redirect&bpo=23057] 中贡献。)

添加了 asyncio.Task.get_coro() 用来获取 asyncio.Task 中的已包装协程。 (由 Alex Grönholm 在 bpo-36999 [https://bugs.python.org/issue?@action=redirect&bpo=36999] 中贡献。)

asyncio 任务现在可以被命名,或者是通过将 name 关键字参数传给 asyncio.create_task()create_task() 事件循环方法,或者是通过在任务对象上调用 set_name() 方法。 任务名称在 asyncio.Taskrepr() 输出中可见,并且还可以使用 get_name() 方法来获取。 (由 Alex Grönholm 在 bpo-34270 [https://bugs.python.org/issue?@action=redirect&bpo=34270] 中贡献。)

将对 Happy Eyeballs [https://en.wikipedia.org/wiki/Happy_Eyeballs] 的支持添加到 asyncio.loop.create_connection()。 要指定此行为,已增加了两个新的形参: happy_eyeballs_delayinterleave。 Happy Eyeballs 算法可提升支持 IPv4 和 IPv6 的应用的响应速度,具体做法是尝试同时使用两者进行连接。 (由 twisteroid ambassador 在 bpo-33530 [https://bugs.python.org/issue?@action=redirect&bpo=33530] 中贡献。)

builtins

内置的 compile() 已改进为可接受 ast.PyCF_ALLOW_TOP_LEVEL_AWAIT 旗标。 当传入此新旗标时,compile() 将允许通常被视为无效语法的最高层级 await, async forasync with 构造。 此后将可返回带有 CO_COROUTINE 旗标的异步代码对象。 (由 Matthias Bussonnier 在 bpo-34616 [https://bugs.python.org/issue?@action=redirect&bpo=34616] 中贡献。)

collections

collections.namedtuple()_asdict() 方法现在将返回 dict 而不是 collections.OrderedDict。 此项更改是由于普通字典自 Python 3.7 起已保证具有确定的元素顺序。 如果还需要 OrderedDict 的额外特性,建议的解决方案是将结果转换为需要的类型: OrderedDict(nt._asdict())。 (由 Raymond Hettinger 在 bpo-35864 [https://bugs.python.org/issue?@action=redirect&bpo=35864] 中贡献。)

cProfile

cProfile.Profile 类现在可被用作上下文管理器。 在运行时对一个代码块实现性能分析:

  1. import cProfile
  2.  
  3. with cProfile.Profile() as profiler:
  4. # 要进行性能分析的代码
  5. ...

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

csv

csv.DictReader 现在将返回 dict 而不是 collections.OrderedDict。 此工具现在会更快速且消耗更少内存同时仍然保留字段顺序。 (由 Michael Selik 在 bpo-34003 [https://bugs.python.org/issue?@action=redirect&bpo=34003] 中贡献。)

curses

添加了一个新变量用于保存下层 ncurses 库的结构版信息: ncurses_version。 (由 Serhiy Storchaka 在 bpo-31680 [https://bugs.python.org/issue?@action=redirect&bpo=31680] 中贡献。)

ctypes

在 Windows 上,CDLL 及其子类现在接受 winmode 形参来指定用于底层 LoadLibraryEx 调用的旗标。 默认旗标被设为仅加载来自可信任位置的 DLL 依赖项,包括 DLL 的存放路径(如果加载初始 DLL 时使用了完整或部分路径)以及通过 add_dll_directory() 添加的路径。 (由 Steve Dower 在 bpo-36085 [https://bugs.python.org/issue?@action=redirect&bpo=36085] 中贡献。)

datetime

添加了新的替代构造器 datetime.date.fromisocalendar()datetime.datetime.fromisocalendar(),它们分别基于 ISO 年份、周序号和周内日序号来构造 datedatetime 对象;这两者分别是其所对应类中 isocalendar 方法的逆操作。 (由 Paul Ganssle 在 bpo-36004 [https://bugs.python.org/issue?@action=redirect&bpo=36004] 中贡献。)

functools

functools.lru_cache() 现在可直接作为装饰器而不是作为返回装饰器的函数。 因此这两种写法现在都被支持:

  1. @lru_cache
  2. def f(x):
  3. @lru_cache(maxsize=256)

  4. def f(x):

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

添加了新的 functools.cached_property() 装饰器,用于在实例生命周期内缓存的已计算特征属性。

  1. import functools
  2. import statistics
  3.  
  4. class Dataset:
  5. def __init__(self, sequence_of_numbers):
  6. self.data = sequence_of_numbers
  7.  
  8. @functools.cached_property
  9. def variance(self):
  10. return statistics.variance(self.data)

(由 Carl Meyer 在 bpo-21145 [https://bugs.python.org/issue?@action=redirect&bpo=21145] 中贡献)

添加了新的 functools.singledispatchmethod() 装饰器可使用 single dispatch 将方法转换为 泛型函数:

  1. from functools import singledispatchmethod
  2. from contextlib import suppress
  3.  
  4. class TaskManager:
  5.  
  6. def __init__(self, tasks):
  7. self.tasks = list(tasks)
  8.  
  9. @singledispatchmethod
  10. def discard(self, value):
  11. with suppress(ValueError):
  12. self.tasks.remove(value)
  13.  
  14. @discard.register(list)
  15. def _(self, tasks):
  16. targets = set(tasks)
  17. self.tasks = [x for x in self.tasks if x not in targets]

(由 Ethan Smith 在 bpo-32380 [https://bugs.python.org/issue?@action=redirect&bpo=32380] 中贡献)

gc

get_objects() 现在能接受一个可选的 generation 形参来指定一个用于获取对象的生成器。 (由 Pablo Galindo 在 bpo-36016 [https://bugs.python.org/issue?@action=redirect&bpo=36016] 中贡献。)

gettext

添加了 pgettext() 及其变化形式。 (由 Franz Glasner, Éric Araujo 和 Cheryl Sabella 在 bpo-2504 [https://bugs.python.org/issue?@action=redirect&bpo=2504] 中贡献。)

gzip

添加 mtime 形参到 gzip.compress() 用于可重现的输出。 (由 Guo Ci Teo 在 bpo-34898 [https://bugs.python.org/issue?@action=redirect&bpo=34898] 中贡献。)

对于特定类型的无效或已损坏 gzip 文件现在将引发 BadGzipFile 而不是 OSError。 (由 Filip Gruszczyński, Michele Orrù 和 Zackery Spytz 在 bpo-6584 [https://bugs.python.org/issue?@action=redirect&bpo=6584] 中贡献。)

IDLE 与 idlelib

超过 N 行(默认值为 50)的输出将被折叠为一个按钮。 N 可以在 Settings 对话框的 General 页的 PyShell 部分中进行修改。 数量较少但是超长的行可以通过在输出上右击来折叠。 被折叠的输出可通过双击按钮来展开,或是通过右击按钮来放入剪贴板或是单独的窗口。 (由 Tal Einat 在 bpo-1529353 [https://bugs.python.org/issue?@action=redirect&bpo=1529353] 中贡献。)

在 Run 菜单中增加了 "Run Customized" 以使用自定义设置来运行模块。 输入的任何命令行参数都会被加入 sys.argv。 它们在下次自定义运行时会再次显示在窗体中。 用户也可以禁用通常的 Shell 主模块重启。 (由 Cheryl Sabella, Terry Jan Reedy 等人在 bpo-5680 [https://bugs.python.org/issue?@action=redirect&bpo=5680] 和 bpo-37627 [https://bugs.python.org/issue?@action=redirect&bpo=37627] 中贡献。)

在 IDLE 编辑器窗口中增加了可选的行号。 窗口打开时默认不显示行号,除非在配置对话框的 General 选项卡中特别设置。 已打开窗口中的行号可以在 Options 菜单中显示和隐藏。 (由 Tal Einat 和 Saimadhav Heblikar 在 bpo-17535 [https://bugs.python.org/issue?@action=redirect&bpo=17535] 中贡献。)

现在会使用 OS 本机编码格式在 Python 字符串和 Tcl 对象间进行转换。 这允许在 IDLE 中处理 emoji 和其他非 BMP 字符。 这些字符将可被显示或是从剪贴板复制和粘贴。 字符串从 Tcl 到 Python 的来回转换现在不会再发生失败。 (过去八年有许多人都为此付出过努力,问题最终由 Serhiy Storchaka 在 bpo-13153 [https://bugs.python.org/issue?@action=redirect&bpo=13153] 中解决。)

在 3.8.1 中新增:

添加切换光标闪烁停止的选项。 (由 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] 中贡献。)

上述修改已被反向移植到 3.7 维护发行版中。

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

inspect

inspect.getdoc() 函数现在可以找到 __slots__ 的文档字符串,如果该属性是一个元素值为文档字符串的 dict 的话。 这提供了类似于目前已有的 property(), classmethod()staticmethod() 等函数的文档选项:

  1. class AudioClip:
  2. __slots__ = {'bit_rate': 'expressed in kilohertz to one decimal place',
  3. 'duration': 'in seconds, rounded up to an integer'}
  4. def __init__(self, bit_rate, duration):
  5. self.bit_rate = round(bit_rate / 1000.0, 1)
  6. self.duration = ceil(duration)

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

io

在开发模式 (-X env) 和 调试编译版 中,io.IOBase 终结器现在会在 close() 方法失败时将异常写入日志。 发生的异常在发布编译版中会被静默地忽略。 (由 Victor Stinner 在 bpo-18748 [https://bugs.python.org/issue?@action=redirect&bpo=18748] 中贡献。)

itertools

itertools.accumulate() 函数增加了可选的 initial 关键字参数用来指定一个初始值:

  1. >>> from itertools import accumulate
  2. >>> list(accumulate([10, 5, 30, 15], initial=1000))
  3. [1000, 1010, 1015, 1045, 1060]

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

json.tool

添加选项 --json-lines 用于将每个输入行解析为单独的 JSON 对象。 (由 Weipeng Hong 在 bpo-31553 [https://bugs.python.org/issue?@action=redirect&bpo=31553] 中贡献。)

logging

logging.basicConfig() 添加了 force 关键字参数。 当设为真值时,关联到根日志记录器的任何现有处理器都将在执行由其他参数所指定的配置之前被移除并关闭。

这解决了一个长期存在的问题。 当一个日志处理器或 basicConfig() 被调用时,对 basicConfig() 的后续调用会被静默地忽略。 这导致使用交互提示符或 Jupyter 笔记本更新、试验或讲解各种日志配置选项变得相当困难。

(由 Raymond Hettinger 提议,由 Donghee Na 实现,并由 Vinay Sajip 审核后在 bpo-33897 [https://bugs.python.org/issue?@action=redirect&bpo=33897] 中贡献。)

math

添加了新的函数 math.dist() 用于计算两点之间的欧几里得距离。 (由 Raymond Hettinger 在 bpo-33089 [https://bugs.python.org/issue?@action=redirect&bpo=33089] 中贡献。)

扩展了 math.hypot() 函数以便处理更多的维度。 之前它仅支持 2-D 的情况。 (由 Raymond Hettinger 在 bpo-33089 [https://bugs.python.org/issue?@action=redirect&bpo=33089] 中贡献。)

添加了新的函数 math.prod() 作为的 sum() 同类,该函数返回 'start' 值 (默认值: 1) 乘以一个数字可迭代对象的积:

  1. >>> prior = 0.8
  2. >>> likelihoods = [0.625, 0.84, 0.30]
  3. >>> math.prod(likelihoods, start=prior)
  4. 0.126

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

添加了两个新的组合函数 math.perm()math.comb():

  1. >>> math.perm(10, 3) # 10 项每次取 3 项的排列
  2. 720
  3. >>> math.comb(10, 3) # 10 项每次取 3 项的组合
  4. 120

(由 Yash Aggarwal, Keller Fuchs, Serhiy Storchaka 和 Raymond Hettinger 在 bpo-37128 [https://bugs.python.org/issue?@action=redirect&bpo=37128], bpo-37178 [https://bugs.python.org/issue?@action=redirect&bpo=37178] 和 bpo-35431 [https://bugs.python.org/issue?@action=redirect&bpo=35431] 中贡献。)

添加了一个新函数 math.isqrt() 用于计算精确整数平方根而无需转换为浮点数。 该新函数支持任意大整数。 它的执行速度比 floor(sqrt(n)) 快但是比 math.sqrt() 慢:

  1. >>> r = 650320427
  2. >>> s = r ** 2
  3. >>> isqrt(s - 1) # 正确
  4. 650320426
  5. >>> floor(sqrt(s - 1)) # 不正确
  6. 650320427

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

函数 math.factorial() 不再接受非整数类参数。 (由 Pablo Galindo 在 bpo-33083 [https://bugs.python.org/issue?@action=redirect&bpo=33083] 中贡献。)

mmap

mmap.mmap 类现在具有一个 madvise() 方法用于访问 madvise() 系统调用。 (由 Zackery Spytz 在 bpo-32941 [https://bugs.python.org/issue?@action=redirect&bpo=32941] 中贡献。)

multiprocessing

添加了新的 multiprocessing.shared_memory 模块。 (由 Davin Potts 在 bpo-35813 [https://bugs.python.org/issue?@action=redirect&bpo=35813] 中贡献。)

在macOS上,现在默认使用的启动方式是spawn启动方式。 (由 Victor Stinner 在 bpo-33725 [https://bugs.python.org/issue?@action=redirect&bpo=33725] 中贡献。)

os

在 Windows 上添加了新函数 add_dll_directory() 用于在导入扩展模块或使用 ctypes 加载 DLL 时为本机依赖提供额外搜索路径 。 (由 Steve Dower 在 bpo-36085 [https://bugs.python.org/issue?@action=redirect&bpo=36085] 中贡献。)

添加了新的 os.memfd_create() 函数用于包装 memfd_create() 系统调用。 (由 Zackery Spytz 和 Christian Heimes 在 bpo-26836 [https://bugs.python.org/issue?@action=redirect&bpo=26836] 中贡献。)

在 Windows 上,大部分用于处理重解析点,(包括符号链接和目录连接)的手动逻辑已被委托给操作系统。 特别地,os.stat() 现在将会遍历操作系统所支持的任何内容,而 os.lstat() 将只打开被标识为“名称代理”的重解析点,而其要由 os.stat() 打开其他的重解析点。 在所有情况下,stat_result.st_mode 将只为符号链接而非其他种类的重解析点设置 S_IFLNK。 要标识其他种类的重解析点,请检查新的 stat_result.st_reparse_tag 属性。

在 Windows 上,os.readlink() 现在能够读取目录连接。 请注意 islink() 会对目录连接返回 False,因此首先检查 islink 的代码将连续把连接视为目录,而会处理 os.readlink() 所引发错误的代码现在会把连接视为链接。

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

os.path

返回布尔值结果的 os.path 函数例如 exists(), lexists(), isdir(), isfile(), islink(), 以及 ismount() 现在对于包含在 OS 层级无法表示的字符或字节的路径将会返回 False 而不是引发 ValueError 或其子类 UnicodeEncodeErrorUnicodeDecodeError。 (由 Serhiy Storchaka 在 bpo-33721 [https://bugs.python.org/issue?@action=redirect&bpo=33721] 中贡献。)

expanduser() 在 Windows 上现在改用 USERPROFILE 环境变量而不再使用 HOME,后者通常不会为一般用户账户设置。 (由 Anthony Sottile 在 bpo-36264 [https://bugs.python.org/issue?@action=redirect&bpo=36264] 中贡献。)

isdir() 在 Windows 上将不再为不存在的目录的链接返回 True

realpath() 在 Windows 上现在会识别重解析点,包括符号链接和目录连接。

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

pathlib

返回布尔值结果的 pathlib.Path 方法例如 exists(), is_dir(), is_file(), is_mount(), is_symlink(), is_block_device(), is_char_device(), is_fifo(), is_socket() 现在对于包含在 OS 层级上无法表示的字符的路径将返回 False 而不是引发 ValueError 或其子类 UnicodeEncodeError。 (由 Serhiy Storchaka 在 bpo-33721 [https://bugs.python.org/issue?@action=redirect&bpo=33721] 中贡献。).)

增加了 pathlib.Path.link_to(),它可以创建指向一个路径的硬链接。 (由 Joannah Nanjekye 在 bpo-26978 [https://bugs.python.org/issue?@action=redirect&bpo=26978] 中贡献) 请注意 link_to 在 3.10 中被弃用并在 3.12 中被移除并改用在 3.10 中添加的 hardlink_to 方法,它与现有 symlink_to 方法的语义相匹配。

pickle

pickle 扩展子类化针对 C 优化的 Pickler 现在可通过定义特殊的 reducer_override() 方法来重载函数和类的封存逻辑。 (由 Pierre Glaser 和 Olivier Grisel 在 bpo-35900 [https://bugs.python.org/issue?@action=redirect&bpo=35900] 中贡献。)

plistlib

添加了新的 plistlib.UID 并启动了对读取和写入经过 NSKeyedArchiver 编码的二进制 plists 的支持。 (由 Jon Janzen 在 bpo-26707 [https://bugs.python.org/issue?@action=redirect&bpo=26707] 中贡献。)

pprint

pprint 模块为一些函数添加了 sort_dicts 形参。 默认情况下,这些函数会继续在渲染或打印之前对字典进行排序。 但是,如果 sort_dicts 设为假值,则字典将保持键插入时的顺序。 这在调试期间与 JSON 输入进行比较时会很有用。

除此之外,还增加了一个方便的新函数 pprint.pp(),它类似于 pprint.pprint() 但它的 sort_dicts 默认为 False:

  1. >>> from pprint import pprint, pp
  2. >>> d = dict(source='input.txt', operation='filter', destination='output.txt')
  3. >>> pp(d, width=40) # 原始顺序
  4. {'source': 'input.txt',
  5. 'operation': 'filter',
  6. 'destination': 'output.txt'}
  7. >>> pprint(d, width=40) # 键按字母顺序排序
  8. {'destination': 'output.txt',
  9. 'operation': 'filter',
  10. 'source': 'input.txt'}

(由 Rémi Lapeyre 在 bpo-30670 [https://bugs.python.org/issue?@action=redirect&bpo=30670] 中贡献。)

py_compile

py_compile.compile() 现在支持静默模式。 (由 Joannah Nanjekye 在 bpo-22640 [https://bugs.python.org/issue?@action=redirect&bpo=22640] 中贡献。)

shlex

新增了 shlex.join() 函数作为 shlex.split() 的逆操作。 (由 Bo Bayles 在 bpo-32102 [https://bugs.python.org/issue?@action=redirect&bpo=32102] 中贡献。)

shutil

shutil.copytree() 现在接受新的 dirs_exist_ok 关键字参数。 (由 Josh Bronson 在 bpo-20849 [https://bugs.python.org/issue?@action=redirect&bpo=20849] 中贡献。)

shutil.make_archive() 现在对新的归档默认使用 modern pax (POSIX.1-2001) 格式以提升可移植性和标准一致性,此特性继承自对 tarfile 模块的相应更改。 (由 C.A.M. Gerlach 在 bpo-30661 [https://bugs.python.org/issue?@action=redirect&bpo=30661] 中贡献。)

shutil.rmtree() 在 Windows 上现在会移除目录连接而不会递归地先移除其中的内容。 (由 Steve Dower 在 bpo-37834 [https://bugs.python.org/issue?@action=redirect&bpo=37834] 中贡献。)

socket

添加了 create_server()has_dualstack_ipv6() 便捷函数以自动化在创建服务器套接字时通常情况下所必须的任务,包括在同一套接字中同时接受 IPv4 和 IPv6 连接。 (由 Giampaolo Rodolà 在 bpo-17561 [https://bugs.python.org/issue?@action=redirect&bpo=17561] 中贡献。).)

socket.if_nameindex(), socket.if_nametoindex()socket.if_indextoname() 函数已经在 Windows 上实现。 (由 Zackery Spytz 在 bpo-37007 [https://bugs.python.org/issue?@action=redirect&bpo=37007] 中贡献。)

ssl

增加了 post_handshake_authverify_client_post_handshake() 分别启用和初始化 TLS 1.3 握手后验证。 (由 Christian Heimes 在 bpo-34670 [https://bugs.python.org/issue?@action=redirect&bpo=34670] 中贡献。)

statistics

增加了 statistics.fmean() 作为 statistics.mean() 的更快速的浮点数版本。 (由 Raymond Hettinger 和 Steven D'Aprano 在 bpo-35904 [https://bugs.python.org/issue?@action=redirect&bpo=35904] 中贡献。)

增加了 statistics.geometric_mean() (由 Raymond Hettinger 在 bpo-27181 [https://bugs.python.org/issue?@action=redirect&bpo=27181] 中贡献。)

添加了 statistics.multimode() 用于返回最常见值的列表。 (由 Raymond Hettinger 在 bpo-35892 [https://bugs.python.org/issue?@action=redirect&bpo=35892] 中贡献。)

添加了 statistics.quantiles() 用于将数据或分布划分为多个等概率区间(例如四分位、十分位或百分位)。 (由 Raymond Hettinger 在 bpo-36546 [https://bugs.python.org/issue?@action=redirect&bpo=36546] 中贡献。)

添加了 statistics.NormalDist 用于创建和操纵随机变量的正态分布。 (由 Raymond Hettinger 在 bpo-36018 [https://bugs.python.org/issue?@action=redirect&bpo=36018] 中贡献。)

  1. >>> temperature_feb = NormalDist.from_samples([4, 12, -3, 2, 7, 14])
  2. >>> temperature_feb.mean
  3. 6.0
  4. >>> temperature_feb.stdev
  5. 6.356099432828281
  6.  
  7. >>> temperature_feb.cdf(3) # 在 3 度以下的几率
  8. 0.3184678262814532
  9. >>> # 为 7 度与为 10 度的相对几率
  10. >>> temperature_feb.pdf(7) / temperature_feb.pdf(10)
  11. 1.2039930378537762
  12.  
  13. >>> el_niño = NormalDist(4, 2.5)
  14. >>> temperature_feb += el_niño # 加入一个气候效应
  15. >>> temperature_feb
  16. NormalDist(mu=10.0, sigma=6.830080526611674)
  17.  
  18. >>> temperature_feb * (9/5) + 32 # 转换为华氏度
  19. NormalDist(mu=50.0, sigma=12.294144947901014)
  20. >>> temperature_feb.samples(3) # 生成随机样本
  21. [7.672102882379219, 12.000027119750287, 4.647488369766392]

sys

添加了新的 sys.unraisablehook() 函数,可被重载以便控制如何处理“不可引发的异常”。 它会在发生了一个异常但 Python 没有办法处理时被调用。 例如,当一个析构器在垃圾回收时 (gc.collect()) 所引发的异常。 (由 Victor Stinner 在 bpo-36829 [https://bugs.python.org/issue?@action=redirect&bpo=36829] 中贡献。)

tarfile

tarfile 模块现在对新的归档默认使用 modern pax (POSIX.1-2001) 格式而不再是之前的 GNU 专属格式。 这通过标准化和可扩展格式的统一编码 (UTF-8) 提升了跨平台可移植性,还提供了其他一些益处。 (由 C.A.M. Gerlach 在 bpo-36268 [https://bugs.python.org/issue?@action=redirect&bpo=36268] 中贡献。)

threading

添加了新的 threading.excepthook() 函数用来处理未捕获的 threading.Thread.run() 异常。 它可被重载以便控制如何处理未捕获的 threading.Thread.run() 异常。 (由 Victor Stinner 在 bpo-1230540 [https://bugs.python.org/issue?@action=redirect&bpo=1230540] 中贡献。)

添加了新的 threading.get_native_id() 函数以及 threading.Thread 类的 native_id 属性。 它们会返回内核所分配给当前线程的原生整数线程 ID。 此特性仅在特定平台上可用,参见 get_native_id 了解详情。 (由 Jake Tesler 在 bpo-36084 [https://bugs.python.org/issue?@action=redirect&bpo=36084] 中贡献。)

tokenize

当提供不带末尾新行的输入时,tokenize 模块现在会隐式地添加 NEWLINE 形符。 此行为现在已与 C 词法分析器的内部行为相匹配。 (由 Ammar Askar 在 bpo-33899 [https://bugs.python.org/issue?@action=redirect&bpo=33899] 中贡献。)

tkinter

tkinter.Spinbox 中添加了方法 selection_from(), selection_present(), selection_range()selection_to()。 (由 Juliette Monsel 在 bpo-34829 [https://bugs.python.org/issue?@action=redirect&bpo=34829] 中贡献。)

tkinter.Canvas 类中添加了方法 moveto()。 (由 Juliette Monsel 在 bpo-23831 [https://bugs.python.org/issue?@action=redirect&bpo=23831] 中贡献。)

tkinter.PhotoImage 类现在具有 transparency_get()transparency_set() 方法。 (由 Zackery Spytz 在 bpo-25451 [https://bugs.python.org/issue?@action=redirect&bpo=25451] 中贡献。)

time

为 macOS 10.12 添加了新的时钟 CLOCK_UPTIME_RAW。 (由 Joannah Nanjekye 在 bpo-35702 [https://bugs.python.org/issue?@action=redirect&bpo=35702] 中贡献。)

typing

typing 模块加入了一些新特性:

  1. class Location(TypedDict, total=False):
  2. lat_long: tuple
  3. grid_square: str
  4. xy_coordinate: tuple
  1. def get_status(port: int) -> Literal['connected', 'disconnected']:
  2. ...
  1. pi: Final[float] = 3.1415926536

unicodedata

unicodedata 模块现在已升级为使用 Unicode 12.1.0 [https://blog.unicode.org/2019/05/unicode-12-1-en.html] 发布版。

新的函数 is_normalized() 可被用来验证字符串是否为特定正规形式,通常会比实际进行字符串正规化要快得多。 (由 Max Belanger, David Euresti 和 Greg Price 在 bpo-32285 [https://bugs.python.org/issue?@action=redirect&bpo=32285] 和 bpo-37966 [https://bugs.python.org/issue?@action=redirect&bpo=37966] 中贡献。)

unittest

添加了 AsyncMock 以支持异步版本的 Mock。 同时也添加了相应的断言函数用于测试。 (由 Lisa Roach 在 bpo-26467 [https://bugs.python.org/issue?@action=redirect&bpo=26467] 中贡献。)

为 unittest 添加 了 addModuleCleanup()addClassCleanup() 以支持对 setUpModule()setUpClass() 进行清理。 (由 Lisa Roach 在 bpo-24412 [https://bugs.python.org/issue?@action=redirect&bpo=24412] 中贡献。)

一些模拟断言函数现在也会在失败时打印一个实际调用列表。 (由 Petter Strandmark 在 bpo-35047 [https://bugs.python.org/issue?@action=redirect&bpo=35047] 中贡献。)

unittest 模块已支持通过 unittest.IsolatedAsyncioTestCase 来使用协程作为测试用例。 (由 Andrew Svetlov 在 bpo-32972 [https://bugs.python.org/issue?@action=redirect&bpo=32972] 中贡献。)

示例:

  1. import unittest
  2.  
  3.  
  4. class TestRequest(unittest.IsolatedAsyncioTestCase):
  5.  
  6. async def asyncSetUp(self):
  7. self.connection = await AsyncConnection()
  8.  
  9. async def test_get(self):
  10. response = await self.connection.get("https://example.com")
  11. self.assertEqual(response.status_code, 200)
  12.  
  13. async def asyncTearDown(self):
  14. await self.connection.close()
  15.  
  16.  
  17. if __name__ == "__main__":
  18. unittest.main()

venv

现在 venv 在所有平台上都会包含 Activate.ps1 脚本用于在 PowerShell Core 6.1 下激活虚拟环境。 (由 Brett Cannon 在 bpo-32718 [https://bugs.python.org/issue?@action=redirect&bpo=32718] 中贡献。)

weakref

weakref.proxy() 返回的代理对象现在除其他算术运算符外也支持矩阵乘法运算符 @@=。 (由 Mark Dickinson 在 bpo-36669 [https://bugs.python.org/issue?@action=redirect&bpo=36669] 中贡献。)

xml

作为对 DTD 和外部实体检索的缓解,在默认情况下 xml.dom.minidomxml.sax 模块不再处理外部实体。 (由 Christian Heimes 在 bpo-17239 [https://bugs.python.org/issue?@action=redirect&bpo=17239] 中贡献。)

xml.etree.ElementTree 模块中的 .find*() 方法支持通配符搜索例如 {*}tag,此搜索会忽略命名空间以及返回给定命名空间中所有标签的 {namespace}*。 (由 Stefan Behnel 在 bpo-28238 [https://bugs.python.org/issue?@action=redirect&bpo=28238] 中贡献。)

xml.etree.ElementTree 模块提供了一个实现 C14N 2.0 的新函数 –xml.etree.ElementTree.canonicalize()。 (由 Stefan Behnel 在 bpo-13611 [https://bugs.python.org/issue?@action=redirect&bpo=13611] 中贡献。)

xml.etree.ElementTree.XMLParser 的目标对象可通过新的回调方法 start_ns()end_ns() 来接受命名空间声明事件。 此外,xml.etree.ElementTree.TreeBuilder 目标可被配置为处理有关注释和处理指令事件以将它们包含在所生成的树当中。 (由 Stefan Behnel 在 bpo-36676 [https://bugs.python.org/issue?@action=redirect&bpo=36676] 和 bpo-36673 [https://bugs.python.org/issue?@action=redirect&bpo=36673] 中贡献。)

xmlrpc

xmlrpc.client.ServerProxy 现在支持可选的 headers 关键字参数作为随同每次请求发送的 HTTP 标头序列。 此特征的作用之一是使得从默认的基础认证升级到更快速的会话认证成为可能。 (由 Cédric Krier 在 bpo-35153 [https://bugs.python.org/issue?@action=redirect&bpo=35153] 中贡献。)

性能优化

  • subprocess 模块现在能在某些情况下使用 os.posix_spawn() 函数以获得更好的性能。 目前,它的使用仅限 macOS 和 Linux(使用 glibc 2.24 或更新版本),并要求满足以下条件:

    • close_fds 为假值;

    • preexec_fn, pass_fds, cwd 和 start_new_session 形参未设置;

    • executable 路径包含一个目录。

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

构建和 C API 的改变

改变的例子:

  • 只会安装 python3.8 程序,不再有 python3.8m 程序。

  • 只会安装 python3.8-config 脚本,不再有 python3.8m-config 脚本。

  • m 旗标已经从动态库文件名后缀中移除:包括标准库中的扩展模块以及第三方包所产生和安装的模块例如从 PyPI 下载的模块。 以 Linux 为例,Python 3.7 的后缀 .cpython-37m-x86_64-linux-gnu.so 在 Python 3.8 中改为 .cpython-38-x86_64-linux-gnu.so

  • 重新组织了所有头文件以更好地区分不同种类的 API:

    • Include/*.h 应为可移植的公有稳定版 C API。

    • Include/cpython/*.h 应为 CPython 专属的不稳定版 C API;公有 API,部分私有 API 附加 _Py or _PY 前缀。

    • Include/internal/*.h 应为 CPython 特别专属的私有内部 C API。 此 API 不具备向下兼容保证并且不应在 CPython 以外被使用。 它们的公开仅适用于特别限定的需求例如调试器和性能分析等必须直接访问 CPython 内部数据而不通过调用函数的应用。 此 API 现在是通过 make install 安装的。

(由 Victor Stinner 在 bpo-35134 [https://bugs.python.org/issue?@action=redirect&bpo=35134] 和 bpo-35081 [https://bugs.python.org/issue?@action=redirect&bpo=35081] 中贡献,相关工作由 Eric Snow 在 Python 3.7 中发起。)

  • 某些宏已被转换为静态内联函数:形参类型和返回类型定义良好,它们不再会有与宏相关的问题,变量具有局部作用域。 例如:

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

  • PyByteArray_Init()PyByteArray_Fini() 函数已被移除。 它们自 Python 2.7.4 和 Python 3.2.0 起就没有任何用处,被排除在受限 API (稳定 ABI) 之外,并且未被写入文档。 (由 Victor Stinner 在 bpo-35713 [https://bugs.python.org/issue?@action=redirect&bpo=35713] 中贡献。)

  • PyExceptionClass_Name() 的结果类型现在是 const char * 而非 char *。 (由 Serhiy Storchaka 在 bpo-33818 [https://bugs.python.org/issue?@action=redirect&bpo=33818] 中贡献。)

  • Modules/Setup.distModules/Setup 两者的共存已被移除。 之前在更新 CPython 源码树时,开发者必须手动拷贝 Modules/Setup.dist (在源码树内) 到 Modules/Setup (在构建树内) 以反映上游的任何改变。 旧特性对打包者来说有一点益处,但代价是对追踪 CPython 开发进程的开发者造成经常性的麻烦,因为忘记拷贝该文件可能导致构建失败。

现在构建系统总是会从源码树内的 Modules/Setup 读取数据。 建议希望定制该文件的开发者在 CPython 的一个 git 分叉或补丁文件中维护他们的更改,就如他们对源码树做任何其他改变时一样。

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

弃用

这些方法的实现会忽略它们的 index 形参,并改为返回下一条目。 (由 Berker Peksag 在 bpo-9372 [https://bugs.python.org/issue?@action=redirect&bpo=9372] 中贡献。)

函数 bind_textdomain_codeset(),方法 output_charset()set_output_charset(),以及函数 translation()install() 的 codeset 形参也已被弃用,因为它们仅适用于 l*gettext() 函数。 (由 Serhiy Storchaka 在 bpo-33710 [https://bugs.python.org/issue?@action=redirect&bpo=33710] 中贡献。)

在未来版本的 Python 中,它们将成为 仅限位置参数。 (由 Serhiy Storchaka 在 bpo-36492 [https://bugs.python.org/issue?@action=redirect&bpo=36492] 中贡献。)

API 与特性的移除

下列特性与 API 已从 Python 3.8 中移除:

移植到 Python 3.8

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

Python 行为的改变

Python API 的变化

  • 在 Windows 上对扩展模块的 DLL 依赖以及通过 ctypes 加载的 DLL 的解析现在将更为安全。 只有系统路径、包含相信 DLL 或 PYD 文件的路径以及通过 add_dll_directory() 添加的目录才会被作为加载时依赖的搜索位置。 特别地, PATH 和当前工作目录将不再被使用,对它们的修改将不再对正常的 DLL 解析产生影响。 如果你的应用依赖于这些机制,你应当先检查 add_dll_directory(),如果它存在就用它在加载你的库时添加你的 DLL 目录。 请注意 Windows 7 用户还需要确保 Windows 更新包 KB2533623 已安装(这一点也会由安装器进行验证)。 (由 Steve Dower 在 bpo-36085 [https://bugs.python.org/issue?@action=redirect&bpo=36085] 中贡献。)

  • 关联到 pgen 的头文件和函数在其被纯 Python 实现取代后已被移除。 (由 Pablo Galindo 在 bpo-36623 [https://bugs.python.org/issue?@action=redirect&bpo=36623] 中贡献。)

  • types.CodeType 在构造器的第二个位置新增了一个形参 (posonlyargcount) 以支持在 PEP 570 [https://peps.python.org/pep-0570/] 中定义的仅限位置参数。 第一个参数 (argcount) 现在表示位置参数的总数量 (包括仅限位置参数)。 types.CodeType 中新增的 replace() 方法可用于让代码支持 future 特性。

  • hmac.new()digestmod 形参不再默认使用 MD5 摘要。

C API 的变化

静态分配类型 将不受影响。

在大部分情况下,这应该都不会有附带影响。 但是,在分配实例后手动提升引用计数的类型(也许是为了绕过漏洞)现在可能永远不会被销毁。 要避免这种情况,这些类需要在实例撤销分配期间在类型对象上调用 Py_DECREF。

要正确地将这些类型移植到 3.8,请应用以下修改:

示例:

  1. static foo_struct *
  2. foo_new(PyObject *type) { foo_struct *foo = PyObject_GC_New(foo_struct, (PyTypeObject *) type); if (foo == NULL) return NULL;
  3. #if PY_VERSION_HEX < 0x03080000 // Workaround for Python issue 35810; no longer necessary in Python 3.8 PY_INCREF(type)
  4. #endif return foo;
  5. }
  • 确保所有堆分配类型的自定义 tp_dealloc 函数会减少类型的引用计数。

示例:

  1. static void
  2. foo_dealloc(foo_struct *instance) { PyObject *type = Py_TYPE(instance); PyObject_GC_Del(instance);
  3. #if PY_VERSION_HEX >= 0x03080000 // This was not needed before Python 3.8 (Python issue 35810) Py_DECREF(type);
  4. #endif
  5. }

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

  • Py_DEPRECATED() 宏已经针对 MSVC 实现。 这个宏现在必须放在符号名称之前。

示例:

  1. Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);

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

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

  • 函数 PyNode_AddChild()PyParser_AddToken() 现在接受两个额外的 int 参数 end_lineno 和 end_col_offset。

  • 允许 MinGW 工具直接链接到 python38.dlllibpython38.a 文件已不再包含于标准的 Windows 分发包中。 如果你需要此文件,可使用 gendefdlltool 工具来生成它,这些工具是 MinGW binutils 包的一部分:

  1. gendef - python38.dll > tmp.def
  2. dlltool --dllname python38.dll --def tmp.def --output-lib libpython38.a

已安装的 pythonXY.dll 所在位置将取决于安装选项以及 Windows 的版本和语言。 请参阅 在Windows上使用 Python 了解更多信息。 该结果库应当放在与 pythonXY.lib 相同的目录下,这通常是你的 Python 安装路径下的 libs 目录。

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

CPython 字节码的改变

  • 解释器循环已通过将块堆栈展开逻辑移入编译器获得了简化。 编译器现在会发出显式指令来调整值堆栈并为 break, continuereturn 调用清除代码。

移除了操作码 BREAK_LOOP, CONTINUE_LOOP, SETUP_LOOPSETUP_EXCEPT。 添加了新的操作码 ROT_FOUR, BEGIN_FINALLY, CALL_FINALLYPOP_FINALLY。 修改了 END_FINALLYWITH_CLEANUP_START 的行为。

(由 Mark Shannon, Antoine Pitrou 和 Serhiy Storchaka 在 bpo-17611 [https://bugs.python.org/issue?@action=redirect&bpo=17611] 中贡献。)

演示和工具

添加了一个检测脚本用于对访问变量的不同方式进行计时: Tools/scripts/var_access_benchmark.py。 (由 Raymond Hettinger 在 bpo-35884 [https://bugs.python.org/issue?@action=redirect&bpo=35884] 中贡献。)

以下是自 Python 3.3 以来性能提升情况的总结:

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

基准测试是在 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 位编译版得到的数据。 该基准测试脚本显示时间以纳秒为单位。

Python 3.8.1 中的重要变化

出于重要的安全性考量,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] 中贡献。。)

Python 3.8.2 中的重要变化

修复了 shutil.copytree()ignore 回调中的回归问题。 现在该参数的类型已改回 str 和 List[str]。 (由 Manuel Barkhau 和 Giampaolo Rodola 在 gh-83571 [https://github.com/python/cpython/issues/83571] 中贡献。)

Python 3.8.3 中的重要变化

__future__ 模块中的 future 旗标常量值已被更新以防止与编译器旗标发生冲突。 在之前版本中 PyCF_ALLOW_TOP_LEVEL_AWAITCO_FUTURE_DIVISION 存在冲突。 (由 Batuhan Taskaya 在 gh-83743 [https://github.com/python/cpython/issues/83743] 中贡献。)

Python 3.8.8 中的重要变化

较早的 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.8.9 中的重要变化

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

Python 3.8.10 中的重要变化

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

对于 3.8.10,Python 现在支持在 macOS 11 (Big Sur) 和 Apple Silicon Mac (基于 ARM64 架构) 上构建和运行。 现在提供了一个新的通用构建形式 universal2 能够在一组可执行文件上同时原生支持 ARM64Intel 64。 请注意对 "弱链接" 的支持,即以较新 macOS 版本为目标构建的二进制文件通过在运行时测试缺失特性的方式也能在较旧版本上正确运行,尚未被包括在本次对 Python 3.9 的反向移植中;要支持更多的 macOS 版本,请继续以设定版本区间的最旧版本作为目标进行构建。

(最初由 Ronald Oussoren 和 Lawrence D'Anna 在 gh-85272 [https://github.com/python/cpython/issues/85272] 中贡献,由 FX Coudert 和 Eli Rykoff 提供修正,并由 Maxime Bélanger 和 Ned Deily 反向移植到 3.8)

Python 3.8.10 中的重要变化

urllib.parse

在 URL 中存在换行符或制表符,可能会导致某些形式的攻击。根据 WHATWG 的规范更新了:rfc:3986urllib.parse 中的解析器将从 URL 中移除 ASCII 换行符 \n\r\t字符,以防止这种攻击。将移除的字符由一个新的模块级变量urllib.parse._UNSAFE_URL_BYTES_TO_REMOVE 控制。(参阅 bpo-43882 [https://bugs.python.org/issue?@action=redirect&bpo=43882] )

Python 3.8.12 中的重要变化

Python API 的变化

从 Python 3.8.12 开始 ipaddress 模块不再接受 IPv4 地址字符串中有任何前缀的零。 前缀的零有歧义且会被某些库解读为八进制数字。 例如旧版函数 socket.inet_aton() 就将前缀的零视为八进制数字。 最新 inet_pton() 的 glibc 实现则不接受任何前缀的零。

(最初由 Christian Heimes 在 bpo-36384 [https://bugs.python.org/issue?@action=redirect&bpo=36384] 中贡献,并由 Achraf Merzouki 向下移植到 3.8。)

3.8.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.8.17 中的重要变化

tarfile