Python 3.8 的新特性 — Python 文档

来自菜鸟教程
Python/docs/3.9/whatsnew/3.8
跳转至:导航、​搜索

Python 3.8 中的新功能

编辑
雷蒙德·赫廷格

本文解释了 Python 3.8 中与 3.7 相比的新特性。 有关完整详细信息,请参阅 变更日志

摘要 – 发布亮点

新特性

赋值表达式

有新语法 := 将值分配给变量作为更大表达式的一部分。 由于它与 海象 的眼睛和象牙相似,因此被亲切地称为“海象运算符”。

在这个例子中,赋值表达式有助于避免调用 len() 两次:

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

在正则表达式匹配期间会出现类似的好处,其中需要两次匹配对象,一次用于测试是否发生匹配,另一次用于提取子组:

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

该运算符也可用于 while 循环,它计算一个值来测试循环终止,然后在循环体中再次需要相同的值:

# Loop over fixed length blocks
while (block := f.read(256)) != '':
    process(block)

另一个激励用例出现在列表推导式中,其中表达式主体中也需要在过滤条件中计算出的值:

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

尽量限制使用 walrus operator 来清理案例,以降低复杂性并提高可读性。

有关完整说明,请参阅 PEP 572

(由 Emily Morehouse 在 中提供:issue:`35224`。)


仅位置参数

有一个新的函数参数语法 / 指示某些函数参数必须在位置上指定,不能用作关键字参数。 这与 help() 为使用 Larry Hastings 的 Argument Clinic 工具注释的 C 函数显示的符号相同。

在以下示例中,参数 ab 仅是位置参数,而 cd 可以是位置参数或关键字,而 [ X149X]e 或 f 必须是关键字:

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

以下是有效调用:

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

但是,这些是无效调用:

f(10, b=20, c=30, d=40, e=50, f=60)   # b cannot be a keyword argument
f(10, 20, 30, 40, 50, f=60)           # e must be a keyword argument

这种表示法的一个用例是它允许纯 Python 函数完全模拟现有 C 编码函数的行为。 例如,内置的 divmod() 函数不接受关键字参数:

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

另一个用例是在参数名称没有帮助时排除关键字参数。 例如,内置的 len() 函数具有签名 len(obj, /)。 这排除了尴尬的调用,例如:

len(obj='hello')  # The "obj" keyword argument impairs readability

将参数标记为仅位置参数的另一个好处是它允许将来更改参数名称而不会破坏客户端代码的风险。 例如,在statistics模块中,参数名称dist以后可能会发生变化。 这是通过以下功能规范实现的:

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

由于 / 左侧的参数未作为可能的关键字公开,因此参数名称仍可用于 **kwargs

>>> def f(a, b, /, **kwargs):
...     print(a, b, kwargs)
...
>>> f(10, 20, a=1, b=2, c=3)         # a and b are used in two ways
10 20 {'a': 1, 'b': 2, 'c': 3}

这大大简化了需要接受任意关键字参数的函数和方法的实现。 例如,这里是 collections 模块中的代码摘录:

class Counter(dict):

    def __init__(self, iterable=None, /, **kwds):
        # Note "iterable" is a possible keyword argument

有关完整说明,请参阅 PEP 570

(由 Pablo Galindo 在 中提供:issue:`36540`。)


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

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

缓存的位置报告在 sys.pycache_prefixNone 表示默认位置在 __pycache__ 子目录中)。

(由 Carl Meyer 在 中提供:问题:`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 在 中提供:问题:`36465`。)

在 Unix 上,除 Android 和 Cygwin 外,C 扩展不再链接到 libpython。 现在,静态链接的 Python 可以加载使用共享库 Python 构建的 C 扩展。 (由 Victor Stinner 在 中提供:问题:`21536`。)

在 Unix 上,当 Python 在调试模式下构建时,import 现在还会查找在发布模式下编译的 C 扩展以及使用稳定 ABI 编译的 C 扩展。 (由 Victor Stinner 在 中提供:问题:`36722`。)

要将 Python 嵌入到应用程序中,必须将新的 --embed 选项传递给 python3-config --libs --embed 以获取 -lpython3.8(将应用程序链接到 libpython)。 要同时支持 3.8 及更早版本,请先尝试 python3-config --libs --embed,如果前一个命令失败,则回退到 python3-config --libs(没有 --embed)。

添加 pkg-config python-3.8-embed 模块以将 Python 嵌入到应用程序中:pkg-config python-3.8-embed --libs 包括 -lpython3.8。 要同时支持 3.8 及更早版本,请先尝试 pkg-config python-X.Y-embed --libs 并在上一个命令失败时回退到 pkg-config python-X.Y --libs(没有 --embed)(将 X.Y 替换为 Python 版本) )。

另一方面,pkg-config python3.8 --libs 不再包含 -lpython3.8。 C 扩展不能链接到 libpython(Android 和 Cygwin 除外,它们的情况由脚本处理); 此更改是故意向后不兼容的。 (由 Victor Stinner 在 中提供:问题:`36721`。)


f-strings 支持 = 用于自记录表达式和调试

f-strings 添加了 = 说明符。 诸如 f'{expr=}' 之类的 f 字符串将扩展为表达式的文本、等号,然后是已计算表达式的表示。 例如:

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

通常的 f 字符串格式说明符 允许更多地控制表达式结果的显示方式:

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

= 说明符将显示整个表达式,以便可以显示计算:

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

(由埃里克 V 提供。 中的 Smith 和 Larry Hastings:问题:`36817`。)


PEP 578:Python 运行时审计挂钩

PEP 添加了审计挂钩和已验证的开放挂钩。 两者都可以从 Python 和本机代码中获得,允许用纯 Python 代码编写的应用程序和框架利用额外的通知,同时还允许嵌入者或系统管理员部署始终启用审计的 Python 构建。

有关完整详细信息,请参阅 PEP 578


PEP 587:Python 初始化配置

PEP 587 添加了一个新的 C API 来配置 Python 初始化,提供对整个配置的更好控制和更好的错误报告。

新结构:

新功能:

该 PEP 还在这些内部结构中添加了 _PyRuntimeState.preconfigPyPreConfig 类型)和 PyInterpreterState.configPyConfig 类型)字段。 PyInterpreterState.config 成为新的参考配置,替换全局配置变量和其他私有变量。

有关文档,请参阅 Python 初始化配置

有关完整说明,请参阅 PEP 587

(由 Victor Stinner 在 中提供:问题:`36763`。)


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

矢量调用协议被添加到Python/C API。 它旨在形式化已经为各种类完成的现有优化。 任何实现可调用的静态类型都可以使用此协议。

这是目前临时的。 目的是使其在 Python 3.9 中完全公开。

有关完整说明,请参阅 PEP 590

(由 Jeroen Demeyer、Mark Shannon 和 Petr Viktorin 在 :issue:`36974` 中提供。)


带外数据缓冲区的 Pickle 协议 5

pickle 用于在 Python 进程之间传输大数据以利用多核或多机处理时,重要的是通过减少内存副本来优化传输,并可能通过应用自定义技术例如依赖于数据的压缩。

pickle 协议 5 引入了对带外缓冲区的支持,其中 PEP 3118 兼容的数据可以与主pickle 流分开传输,酌情决定通信层的。

有关完整说明,请参阅 PEP 574

(由 Antoine Pitrou 在 中提供:issue:`36785`。)


其他语言更改

  • 由于实现问题,continue 语句在 finally 子句中是非法的。 在 Python 3.8 中,这个限制被取消了。 (由 Serhiy Storchaka 在 中提供:issue:`32489`。)

  • boolintfractions.Fraction 类型现在有一个 as_integer_ratio() 方法,类似于 float[ X146X] 和 decimal.Decimal。 这个次要的 API 扩展使得编写 numerator, denominator = x.as_integer_ratio() 并使其跨多种数字类型工作成为可能。 (由 Lisa Roach 在 :issue:`33073` 和 Raymond Hettinger 在 :issue:`37819` 中贡献。)

  • intfloatcomplex 的构造函数现在将使用 __index__() 特殊方法(如果可用)和相应的方法 __int__()__float__()__complex__() 不可用。 (由 Serhiy Storchaka 在 :issue:`20092` 中提供。)

  • 正则表达式 中添加了对 \N{name} 转义的支持:

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

    (由 Jonathan Eunice 和 Serhiy Storchaka 在 中提供:issue:`30688`。)

  • dict 和 dictviews 现在可以使用 reversed() 以相反的插入顺序迭代。 (由 Rémi Lapeyre 在 中提供:issue:`33462`。)

  • 进一步限制了函数调用中关键字名称的语法。 特别是,不再允许使用 f((keyword)=arg)。 它决不允许在关键字参数赋值项的左侧出现多个裸名。 (由 Benjamin Peterson 在 中提供:issue:`34641`。)

  • yieldreturn 语句中的广义迭代解包不再需要括号。 这使 yieldreturn 语法与正常赋值语法更好地一致:

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

    (由 David Cuthbert 和 Jordan Chapman 在 :issue:`32117` 中提供。)

  • 当诸如 [(10, 20) (30, 40)] 之类的代码中缺少逗号时,编译器会显示一个 SyntaxWarning 并提供有用的建议。 这改进了只有 TypeError 指示第一个元组不可调用。 (由 Serhiy Storchaka 在 中提供:issue:`15248`。)

  • datetime.datedatetime.datetimedatetime.timedelta 对象的子类之间的算术运算现在返回子类的实例,而不是基类。 这也会影响其实现(直接或间接)使用 datetime.timedelta 算法的操作的返回类型,例如 astimezone()。 (由 Paul Ganssle 在 :issue:`32417` 中提供。)

  • 当 Python 解释器被 Ctrl-C (SIGINT) 中断并且产生的 KeyboardInterrupt 异常未被捕获时,Python 进程现在通过 SIGINT 信号或正确的退出代码退出,以便调用进程可以检测到它因 Ctrl-C 而死亡。 POSIX 和 Windows 上的 Shell 使用它来正确终止交互式会话中的脚本。 (由 Google 通过 Gregory P. 中的史密斯:问题:`1054041`。)

  • 一些高级编程风格需要更新现有函数的 types.CodeType 对象。 由于代码对象是不可变的,因此需要创建一个新的代码对象,该对象以现有代码对象为模型。 有 19 个参数,这有点乏味。 现在,新的 replace() 方法可以使用一些更改的参数创建克隆。

    这是一个更改 statistics.mean() 函数以防止将 data 参数用作关键字参数的示例:

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

    (由 Victor Stinner 在 中提供:问题:`37032`。)

  • 对于整数,pow() 函数的三参数形式现在允许在基数与模数互质的情况下指数为负。 然后,当指数为 -1 时,它会计算底数的模倒数,并计算其他负指数的倒数的合适幂。 例如,要计算 38 模 137 的 模乘法逆 ,请编写:

    >>> pow(38, -1, 137)
    119
    >>> 119 * 38 % 137
    1

    线性丢番图方程的解中出现模逆。 例如,要找到 4258𝑥 + 147𝑦 = 369 的整数解,首先重写为 4258𝑥 ≡ 369 (mod 147),然后求解:

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

    (由 Mark Dickinson 在 :issue:`36027` 中贡献。)

  • 字典理解已与字典文字同步,因此首先计算键,然后计算值:

    >>> # Dict comprehension
    >>> cast = {input('role? '): input('actor? ') for i in range(2)}
    role? King Arthur
    actor? Chapman
    role? Black Knight
    actor? Cleese
    
    >>> # Dict literal
    >>> cast = {input('role? '): input('actor? ')}
    role? Sir Robin
    actor? Eric Idle

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

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

    (由 Jörn Heissler 在 中提供:issue:`35224`。)

  • object.__reduce__() 方法现在可以返回一个长度为 2 到 6 个元素的元组。 以前,五个是极限。 新的可选的第六个元素是一个带有 (obj, state) 签名的可调用元素。 这允许直接控制特定对象的状态更新行为。 如果不是 None,这个可调用对象将优先于对象的 __setstate__() 方法。 (由 Pierre Glaser 和 Olivier Grisel 在 中提供:issue:`35900`。)


新模块

  • 新的 importlib.metadata 模块提供(临时)支持从第三方包读取元数据。 例如,它可以提取已安装包的版本号、入口点列表等:

    >>> # Note following example requires that the popular "requests"
    >>> # package has been installed.
    >>>
    >>> from importlib.metadata import version, requires, files
    >>> version('requests')
    '2.22.0'
    >>> list(requires('requests'))
    ['chardet (<3.1.0,>=3.0.2)']
    >>> list(files('requests'))[:5]
    [PackagePath('requests-2.22.0.dist-info/INSTALLER'),
     PackagePath('requests-2.22.0.dist-info/LICENSE'),
     PackagePath('requests-2.22.0.dist-info/METADATA'),
     PackagePath('requests-2.22.0.dist-info/RECORD'),
     PackagePath('requests-2.22.0.dist-info/WHEEL')]

    (由 Barry Warsaw 和 Jason R. 供稿) 中的库姆斯:问题:`34632`。)


改进的模块

AST

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

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

(由 Ivan Levkivskyi 在 :issue:`33416` 中贡献。)

ast.parse() 函数有一些新标志:

  • type_comments=True 使其返回与某些 AST 节点相关联的 PEP 484PEP 526 类型注释的文本;
  • mode='func_type' 可用于解析 PEP 484 “签名类型注释”(函数定义 AST 节点返回);
  • feature_version=(3, N) 允许指定较早的 Python 3 版本。 例如,feature_version=(3, 4) 会将 asyncawait 视为非保留字。

(由 Guido van Rossum 在 中提供:issue:`35766`。)


异步

asyncio.run() 已经从临时 API 升级到稳定 API。 该函数可用于执行协程并在自动管理事件循环的同时返回结果。 例如:

import asyncio

async def main():
    await asyncio.sleep(0)
    return 42

asyncio.run(main())

这是 大约 相当于:

import asyncio

async def main():
    await asyncio.sleep(0)
    return 42

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
    loop.run_until_complete(main())
finally:
    asyncio.set_event_loop(None)
    loop.close()

实际的实现要复杂得多。 因此,asyncio.run() 应该是运行 asyncio 程序的首选方式。

(由 Yury Selivanov 在 :issue:`32314` 中贡献。)

运行 python -m asyncio 会启动一个本机异步 REPL。 这允许对具有顶级 await 的代码进行快速试验。 不再需要直接调用 asyncio.run() ,这会在每次调用时产生一个新的事件循环:

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

(由 Yury Selivanov 在 中提供:issue:`37028`。)

异常 asyncio.CancelledError 现在继承自 BaseException 而不是 Exception,并且不再继承自 concurrent.futures.CancelledError。 (由 Yury Selivanov 在 :issue:`32528` 中贡献。)

在 Windows 上,默认事件循环现在是 ProactorEventLoop。 (由 Victor Stinner 在 中提供:问题:`34687`。)

ProactorEventLoop 现在也支持 UDP。 (由 Adam Meily 和 Andrew Svetlov 在 :issue:`29883` 中贡献。)

ProactorEventLoop 现在可以被 KeyboardInterrupt(“CTRL+C”)打断。 (由 Vladimir Matveev 在 :issue:`23057` 中贡献。)

添加了 asyncio.Task.get_coro(),用于在 asyncio.Task 中获取包装的协程。 (由 Alex Grönholm 在 :issue:`36999` 中提供。)

现在可以命名异步任务,方法是将 name 关键字参数传递给 asyncio.create_task()create_task() 事件循环方法,或者通过调用set_name() 任务对象上的方法。 任务名称在 asyncio.Taskrepr() 输出中可见,也可以使用 get_name() 方法检索。 (由 Alex Grönholm 在 :issue:`34270` 中提供。)

添加了对 Happy Eyeballsasyncio.loop.create_connection() 的支持。 为了指定行为,添加了两个新参数:happy_eyeballs_delayinterleave。 Happy Eyeballs 算法通过尝试同时使用 IPv4 和 IPv6 进行连接来提高支持 IPv4 和 IPv6 的应用程序的响应能力。 (由 中的 twinteroid 大使提供:issue:`33530`。)


内置函数

compile() 内置已改进以接受 ast.PyCF_ALLOW_TOP_LEVEL_AWAIT 标志。 通过这个新标志,compile() 将允许顶级 awaitasync forasync with 构造,这些构造通常被认为是无效的语法。 然后可以返回标有 CO_COROUTINE 标志的异步代码对象。 (由 Matthias Bussonnier 在 中提供:issue:`34616`


收藏

collections.namedtuple()_asdict() 方法现在返回 dict 而不是 collections.OrderedDict。 这是有效的,因为从 Python 3.7 开始,常规字典就保证了排序。 如果需要 OrderedDict 的额外功能,建议的补救措施是将结果转换为所需的类型:OrderedDict(nt._asdict())。 (由 Raymond Hettinger 在 中提供:issue:`35864`。)


个人资料

cProfile.Profile 类现在可以用作上下文管理器。 通过运行来分析代码块:

import cProfile

with cProfile.Profile() as profiler:
      # code to be profiled
      ...

(由 Scott Sanderson 在 中提供:issue:`29235`。)


文件

csv.DictReader 现在返回 dict 的实例而不是 collections.OrderedDict。 该工具现在速度更快,使用的内存更少,同时仍保留字段顺序。 (由 Michael Selik 在 :issue:`34003` 中贡献。)


诅咒

添加了一个保存底层 ncurses 库的结构化版本信息的新变量:ncurses_version。 (由 Serhiy Storchaka 在 中提供:issue:`31680`。)


类型

在 Windows 上,CDLL 和子类现在接受 winmode 参数来指定底层 LoadLibraryEx 调用的标志。 默认标志设置为仅从受信任的位置加载 DLL 依赖项,包括存储 DLL 的路径(如果使用完整或部分路径加载初始 DLL)和由 add_dll_directory() 添加的路径. (由 Steve Dower 在 中提供:issue:`36085`。)


约会时间

添加了新的替代构造函数 datetime.date.fromisocalendar()datetime.datetime.fromisocalendar(),它们分别构造了 datedatetime 对象来自 ISO 年、周数和工作日; 这些是每个类的 isocalendar 方法的逆。 (由 Paul Ganssle 在 :issue:`36004` 中提供。)


功能工具

functools.lru_cache() 现在可以用作直接装饰器,而不是用作返回装饰器的函数。 所以现在支持这两个:

@lru_cache
def f(x):
    ...

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

(由 Raymond Hettinger 在 中提供:issue:`36772`。)

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

import functools
import statistics

class Dataset:
   def __init__(self, sequence_of_numbers):
      self.data = sequence_of_numbers

   @functools.cached_property
   def variance(self):
      return statistics.variance(self.data)

(由 Carl Meyer 在 中提供:issue:`21145`

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

from functools import singledispatchmethod
from contextlib import suppress

class TaskManager:

    def __init__(self, tasks):
        self.tasks = list(tasks)

    @singledispatchmethod
    def discard(self, value):
        with suppress(ValueError):
            self.tasks.remove(value)

    @discard.register(list)
    def _(self, tasks):
        targets = set(tasks)
        self.tasks = [x for x in self.tasks if x not in targets]

(由 Ethan Smith 在 中提供:issue:`32380`


GC

get_objects() 现在可以接收一个可选的 generation 参数,指示从中获取对象的代。 (由 Pablo Galindo 在 中提供:issue:`36016`。)


获取文本

添加了 pgettext() 及其变体。 (由 Franz Glasner、Éric Araujo 和 Cheryl Sabella 在 :issue:`2504` 中提供。)


压缩包

mtime 参数添加到 gzip.compress() 以获得可重现的输出。 (由 Guo Ci Teo 在 中提供:issue:`34898`。)

对于某些类型的无效或损坏的 gzip 文件,现在会引发 BadGzipFile 异常而不是 OSError。 (由 Filip Gruszczyński、Michele Orrù 和 Zackery Spytz 在 :issue:`6584` 中贡献。)


空闲和空闲库

N 行(默认为 50)的输出被压缩到一个按钮。 N 可以在 Settings 对话框的 General 页面的 PyShell 部分中更改。 右键单击输出可以压缩更少但可能超长的行。 可以通过双击按钮或右键单击按钮将压缩的输出扩展到剪贴板或单独的窗口。 (由 Tal Einat 在 中提供:问题:`1529353`。)

将“运行自定义”添加到“运行”菜单以运行具有自定义设置的模块。 输入的任何命令行参数都会添加到 sys.argv。 它们还会重新出现在下一次自定义运行的框中。 还可以抑制正常的Shell主模块重启。 (由 Cheryl Sabella、Terry Jan Reedy 和其他人在 :issue:`5680`:issue:`37627` 中贡献。)

为空闲编辑器窗口添加了可选的行号。 除非在配置对话框的“常规”选项卡中进行了其他设置,否则 Windows 将在没有行号的情况下打开。 现有窗口的行号在选项菜单中显示和隐藏。 (由 Tal Einat 和 Saimadhav Heblikar 在 :issue:`17535` 中提供。)

操作系统本机编码现在用于在 Python 字符串和 Tcl 对象之间进行转换。 这允许 IDLE 使用表情符号和其他非 BMP 字符。 这些字符可以显示或复制粘贴到剪贴板或从剪贴板粘贴。 将字符串从 Tcl 转换为 Python 并返回现在永远不会失败。 (很多人为此工作了八年,但问题最终由 Serhiy Storchaka 在 :issue:`13153` 中解决。)

3.8.1 中的新功能:

添加选项以关闭光标闪烁。 (由 Zackery Spytz 在 中提供:issue:`4603`。)

Escape 键现在关闭 IDLE 完成窗口。 (由 Johnny Najera 在 中提供:issue:`38944`。)

上述更改已向后移植到 3.7 维护版本。

将关键字添加到模块名称完成列表中。 (由特里 J。 中的 Reedy:问题:`37765`。)


检查

inspect.getdoc() 函数现在可以找到 __slots__ 的文档字符串,如果该属性是 dict ,其中值是文档字符串。 这提供了类似于我们已有的 property()classmethod()staticmethod() 的文档选项:

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

(由 Raymond Hettinger 在 中提供:issue:`36326`。)


io

在开发模式 (-X env) 和调试版本中,如果 close() 方法失败,io.IOBase 终结器现在会记录异常。 默认情况下,在发布版本中会以静默方式忽略异常。 (由 Victor Stinner 在 中提供:issue:`18748`。)


迭代工具

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

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

(由 Lisa Roach 在 中提供:问题:`34659`。)


json.tool

添加选项 --json-lines 以将每个输入行解析为单独的 JSON 对象。 (由 Weipeng Hong 在 中提供:issue:`31553`。)


日志记录

force 关键字参数添加到 logging.basicConfig() 设置为 true 时,在执行其他参数指定的配置之前,删除并关闭附加到根记录器的任何现有处理程序.

这解决了一个长期存在的问题。 一旦调用了记录器或 basicConfig(),随后对 basicConfig() 的调用将被默默忽略。 这使得使用交互式提示或 Jupyter 笔记本更新、试验或教授各种日志配置选项变得困难。

(由 Raymond Hettinger 建议,由 Dong-hee Na 实施,并由 Vinay Sajip 在 :issue:`33897` 中审查。)


数学

添加了新函数 math.dist() 用于计算两点之间的欧几里德距离。 (由 Raymond Hettinger 在 中提供:issue:`33089`。)

扩展了 math.hypot() 函数以处理多个维度。 以前,它只支持二维情况。 (由 Raymond Hettinger 在 中提供:issue:`33089`。)

添加了新函数 math.prod(),作为与 sum() 类似的函数,该函数返回“起始”值(默认值:1)乘以可迭代数字的乘积:

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

(由 Pablo Galindo 在 中提供:issue:`35606`。)

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

>>> math.perm(10, 3)    # Permutations of 10 things taken 3 at a time
720
>>> math.comb(10, 3)    # Combinations of 10 things taken 3 at a time
120

(由 Yash Aggarwal、Keller Fuchs、Serhiy Storchaka 和 Raymond Hettinger 在 :issue:`37128`:issue:`37178`:issue:` 中贡献35431`。)

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

>>> r = 650320427
>>> s = r ** 2
>>> isqrt(s - 1)         # correct
650320426
>>> floor(sqrt(s - 1))   # incorrect
650320427

(由 Mark Dickinson 在 :issue:`36887` 中贡献。)

函数 math.factorial() 不再接受类似 int 的参数。 (由 Pablo Galindo 在 中提供:issue:`33083`。)


映射

mmap.mmap 类现在有一个 madvise() 方法来访问 madvise() 系统调用。 (由 Zackery Spytz 在 中提供:issue:`32941`。)


多处理

添加了新的 multiprocessing.shared_memory 模块。 (由 Davin Potts 在 中提供:问题:`35813`。)

在 macOS 上,现在默认使用 spawn 启动方法。 (由 Victor Stinner 在 中提供:问题:`33725`。)


操作系统

在 Windows 上添加了新函数 add_dll_directory(),用于在使用 ctypes 导入扩展模块或加载 DLL 时提供本机依赖项的额外搜索路径。 (由 Steve Dower 在 中提供:issue:`36085`。)

添加了一个新的 os.memfd_create() 函数来封装 memfd_create() 系统调用。 (由 Zackery Spytz 和 Christian Heimes 在 中提供:issue:`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 的代码将继续将连接视为目录,而处理来自 [ X200X]os.readlink() 现在可以将结点视为链接。

(由 Steve Dower 在 中提供:issue:`37834`。)


操作系统路径

os.path 函数返回布尔结果,如 exists(), lexists(), isdir(), isfile ()islink()ismount() 现在返回 False 而不是引发 ValueError 或其子类 UnicodeEncodeErrorUnicodeDecodeError 用于包含无法在操作系统级别表示的字符或字节的路径。 (由 Serhiy Storchaka 在 中提供:issue:`33721`。)

Windows 上的 expanduser() 现在更喜欢 USERPROFILE 环境变量,并且不使用 HOME,通常不会设置对于普通用户帐户。 (由 Anthony Sottile 在 中提供:issue:`36264`。)

isdir() 在 Windows 上不再返回 True 以获取指向不存在目录的链接。

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

(由 Steve Dower 在 中提供:issue:`37834`。)


路径库

pathlib.Path 返回布尔结果的方法,如 exists(), is_dir(), is_file(), is_mount ()is_symlink()is_block_device()is_char_device()、is_fifo()[X224X1]、[isX_socket1] () 现在返回 False 而不是引发 ValueError 或其子类 UnicodeEncodeError 对于包含无法在操作系统级别表示的字符的路径。 (由 Serhiy Storchaka 在 中提供:issue:`33721`。)

添加了 pathlib.Path.link_to(),它创建了一个指向路径的硬链接。 (由 Joannah Nanjekye 在 中提供:issue:`26978`


泡菜

pickle 扩展 C 优化的 Pickle 现在可以通过定义特殊的 reducer_override() 方法来覆盖函数和类的酸洗逻辑。 (由 Pierre Glaser 和 Olivier Grisel 在 中提供:issue:`35900`。)


添加了新的 plistlib.UID 并启用了对读取和写入 NSKeyedArchiver 编码的二进制 plist 的支持。 (由 Jon Janzen 在 中提供:issue:`26707`。)


打印

pprint 模块向多个函数添加了 sort_dicts 参数。 默认情况下,这些函数会在渲染或打印之前继续对字典进行排序。 但是,如果 sort_dicts 设置为 false,则字典会保留插入键的顺序。 这对于在调试期间与 JSON 输入进行比较非常有用。

此外,还有一个方便的新函数,pprint.pp() 类似于 pprint.pprint()sort_dicts 默认为 [ X156X]:

>>> from pprint import pprint, pp
>>> d = dict(source='input.txt', operation='filter', destination='output.txt')
>>> pp(d, width=40)                  # Original order
{'source': 'input.txt',
 'operation': 'filter',
 'destination': 'output.txt'}
>>> pprint(d, width=40)              # Keys sorted alphabetically
{'destination': 'output.txt',
 'operation': 'filter',
 'source': 'input.txt'}

(由 Rémi Lapeyre 在 :issue:`30670` 中提供。)


py_compile

py_compile.compile() 现在支持静默模式。 (由 Joannah Nanjekye 在 :issue:`22640` 中提供。)


shlex

新的 shlex.join() 函数作为 shlex.split() 的逆函数。 (由 Bo Bayles 在 中提供:问题:`32102`。)


休蒂尔

shutil.copytree() 现在接受一个新的 dirs_exist_ok 关键字参数。 (由 Josh Bronson 在 :issue:`20849` 中提供。)

shutil.make_archive() 现在默认为新存档的现代 pax (POSIX.1-2001) 格式,以提高可移植性和标准一致性,继承自 tarfile 模块的相应更改. (由 CAM 提供 中的 Gerlach:问题:`30661`。)

shutil.rmtree() 在 Windows 上现在删除目录连接而不先递归删除它们的内容。 (由 Steve Dower 在 中提供:issue:`37834`。)


插座

添加了 create_server()has_dualstack_ipv6() 便利函数来自动化创建服务器套接字时通常涉及的必要任务,包括在同一套接字上接受 IPv4 和 IPv6 连接。 (由 Giampaolo Rodolà 在 :issue:`17561` 中提供。)

socket.if_nameindex()socket.if_nametoindex()socket.if_indextoname() 函数已在 Windows 上实现。 (由 Zackery Spytz 在 中提供:issue:`37007`。)


ssl

添加了 post_handshake_auth 以启用和 verify_client_post_handshake() 以启动 TLS 1.3 握手后身份验证。 (由 Christian Heimes 在 中提供:issue:`34670`。)


统计数据

添加 statistics.fmean() 作为 statistics.mean() 的更快浮点变体。 (由 Raymond Hettinger 和 Steven D'Aprano 在 :issue:`35904` 中提供。)

添加了 statistics.geometric_mean()(由 Raymond Hettinger 在 中贡献:issue:`27181`。)

添加了 statistics.multimode() 返回最常见值的列表。 (由 Raymond Hettinger 在 中提供:issue:`35892`。)

添加了 statistics.quantiles() 将数据或分布划分为等概率区间(例如 四分位数、十分位数或百分位数)。 (由 Raymond Hettinger 在 中提供:issue:`36546`。)

添加了 statistics.NormalDist,一种用于创建和操作随机变量正态分布的工具。 (由 Raymond Hettinger 在 中提供:issue:`36018`。)

>>> temperature_feb = NormalDist.from_samples([4, 12, -3, 2, 7, 14])
>>> temperature_feb.mean
6.0
>>> temperature_feb.stdev
6.356099432828281

>>> temperature_feb.cdf(3)            # Chance of being under 3 degrees
0.3184678262814532
>>> # Relative chance of being 7 degrees versus 10 degrees
>>> temperature_feb.pdf(7) / temperature_feb.pdf(10)
1.2039930378537762

>>> el_niño = NormalDist(4, 2.5)
>>> temperature_feb += el_niño        # Add in a climate effect
>>> temperature_feb
NormalDist(mu=10.0, sigma=6.830080526611674)

>>> temperature_feb * (9/5) + 32      # Convert to Fahrenheit
NormalDist(mu=50.0, sigma=12.294144947901014)
>>> temperature_feb.samples(3)        # Generate random samples
[7.672102882379219, 12.000027119750287, 4.647488369766392]

系统

添加新的 sys.unraisablehook() 函数,该函数可以被覆盖以控制如何处理“无法引发的异常”。 它在发生异常但 Python 无法处理时调用。 例如,当析构函数引发异常或在垃圾收集期间 (gc.collect())。 (由 Victor Stinner 在 中提供:问题:`36829`。)


tar文件

tarfile 模块现在默认为新存档的现代 pax (POSIX.1-2001) 格式,而不是以前的 GNU 特定格式。 这通过标准化和可扩展格式的一致编码 (UTF-8) 提高了跨平台可移植性,并提供了其他一些好处。 (由 CAM 提供 中的 Gerlach:问题:`36268`。)


穿线

添加一个新的 threading.excepthook() 函数来处理未捕获的 threading.Thread.run() 异常。 它可以被覆盖以控制如何处理未捕获的 threading.Thread.run() 异常。 (由 Victor Stinner 在 中提供:问题:`1230540`。)

threading.Thread 类添加一个新的 threading.get_native_id() 函数和一个 native_id 属性。 它们返回内核分配的当前线程的本机完整线程 ID。 此功能仅在某些平台上可用,有关详细信息,请参阅 get_native_id。 (由 Jake Tesler 在 中提供:issue:`36084`。)


标记化

tokenize 模块现在在提供没有尾随换行符的输入时隐式发出 NEWLINE 令牌。 此行为现在与 C 标记器在内部执行的操作相匹配。 (由 Ammar Askar 在 中提供:issue:`33899`。)


特金特

tkinter.Spinbox 类中添加了方法 selection_from()selection_present()selection_range()selection_to()。 (由 Juliette Monsel 在 中提供:issue:`34829`。)

tkinter.Canvas 类中添加了方法 moveto()。 (由 Juliette Monsel 在 中提供:issue:`23831`。)

tkinter.PhotoImage 类现在有 transparency_get()transparency_set() 方法。 (由 Zackery Spytz 在 中提供:issue:`25451`。)


时间

为 macOS 10.12 添加了新时钟 CLOCK_UPTIME_RAW。 (由 Joannah Nanjekye 在 :issue:`35702` 中提供。)


打字

typing 模块包含几个新功能:


单码数据

unicodedata 模块已升级为使用 Unicode 12.1.0 版本。

新函数 is_normalized() 可用于验证字符串是否为特定的范式,通常比实际对字符串进行规范化要快得多。 (由 Max Belanger、David Euresti 和 Greg Price 在 :issue:`32285`:issue:`37966` 中提供)。


单元测试

添加了 AsyncMock 以支持 Mock 的异步版本。 还添加了用于测试的适当的新断言函数。 (由 Lisa Roach 在 :issue:`26467` 中提供)。

addModuleCleanup()addClassCleanup() 添加到单元测试以支持对 setUpModule()setUpClass() 的清理。 (由 Lisa Roach 在 中提供:问题:`24412`。)

几个模拟断言函数现在还会在失败时打印实际调用列表。 (由 Petter Strandmark 在 中提供:问题:`35047`。)

unittest 模块通过 unittest.IsolatedAsyncioTestCase 获得了对协程用作测试用例的支持。 (由 Andrew Svetlov 在 中提供:issue:`32972`。)

例子:

import unittest


class TestRequest(unittest.IsolatedAsyncioTestCase):

    async def asyncSetUp(self):
        self.connection = await AsyncConnection()

    async def test_get(self):
        response = await self.connection.get("https://example.com")
        self.assertEqual(response.status_code, 200)

    async def asyncTearDown(self):
        await self.connection.close()


if __name__ == "__main__":
    unittest.main()

venv

venv 现在在所有平台上都包含一个 Activate.ps1 脚本,用于在 PowerShell Core 6.1 下激活虚拟环境。 (由 Brett Cannon 在 中贡献:问题:`32718`。)


弱引用

weakref.proxy() 返回的代理对象现在支持矩阵乘法运算符 @@= 以及其他数字运算符。 (由 Mark Dickinson 在 :issue:`36669` 中贡献。)


xml

作为对 DTD 和外部实体检索的缓解,xml.dom.minidomxml.sax 模块默认不再处理外部实体。 (由 Christian Heimes 在 中提供:issue:`17239`。)

xml.etree.ElementTree 模块中的 .find*() 方法支持通配符搜索,例如忽略命名空间的 {*}tag 和返回给定中的所有标签的 {namespace}*命名空间。 (由 Stefan Behnel 在 中提供:issue:`28238`。)

xml.etree.ElementTree 模块提供了一个实现 C14N 2.0 的新函数 –xml.etree.ElementTree.canonicalize()。 (由 Stefan Behnel 在 中提供:issue:`13611`。)

xml.etree.ElementTree.XMLParser的目标对象可以通过新的回调方法start_ns()end_ns()接收命名空间声明事件。 此外,xml.etree.ElementTree.TreeBuilder 目标可以配置为处理关于评论和处理指令的事件,以将它们包含在生成的树中。 (由 Stefan Behnel 在 :issue:`36676`:issue:`36673` 中贡献。)


xmlrpc

xmlrpc.client.ServerProxy 现在支持可选的 headers 关键字参数,用于与每个请求一起发送的 HTTP 标头序列。 除此之外,这使得从默认的基本身份验证升级到更快的会话身份验证成为可能。 (由 Cédric Krier 在 中提供:issue:`35153`。)


优化

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

    • close_fds 为假;

    • preexec_fnpass_fdscwdstart_new_session参数未设置;

    • executable 路径包含一个目录。

    (由 Joannah Nanjekye 和 Victor Stinner 在 中提供:issue:`35537`。)

  • shutil.copyfile()shutil.copy()shutil.copy2()shutil.copytree()]shutil.move() 在 Linux 和 macOS 上使用特定于平台的“快速复制”系统调用,以便更有效地复制文件。 “快速复制”意味着复制操作发生在内核中,避免在 Python 中使用用户空间缓冲区,如“outfd.write(infd.read())”。 在 Windows 上 shutil.copyfile() 使用更大的默认缓冲区大小(1 MiB 而不是 16 KiB)和基于 memoryview()shutil.copyfileobj() 变体使用 。 在同一分区内复制 512 MiB 文件的加速约为 +26% on Linux、+50% on macOS 和 +40% on Windows。 此外,消耗的 CPU 周期要少得多。 请参阅 依赖于平台的高效复制操作 部分。 (由 Giampaolo Rodolà 在 中提供:issue:`33671`。)

  • shutil.copytree() 使用 os.scandir() 函数和所有依赖于它的复制函数使用缓存的 os.stat() 值。 复制包含 8000 个文件的目录的加速约为 +9% on Linux、+20% on Windows 和 +30% on Windows SMB 共享。 os.stat() 系统调用的数量也减少了 38%,使得 shutil.copytree() 在网络文件系统上特别快。 (由 Giampaolo Rodolà 在 中提供:issue:`33695`。)

  • pickle 模块中的默认协议现在是协议 4,首先在 Python 3.4 中引入。 与自 Python 3.0 以来可用的协议 3 相比,它提供了更好的性能和更小的尺寸。

  • PyGC_Head 中移除了一个 Py_ssize_t 成员。 所有 GC 跟踪的对象(例如 元组、列表、字典)大小减少 4 或 8 个字节。 (由 Inada Naoki 在 中提供:issue:`33597`。)

  • uuid.UUID 现在使用 __slots__ 来减少其内存占用。 (由 Wouter Bolsterlee 和 Tal Einat 在 中提供:issue:`30977`

  • operator.itemgetter() 的性能提高了 33%。 优化参数处理并为单个非负整数索引的常见情况添加到元组中的快速路径(这是标准库中的典型用例)。 (由 Raymond Hettinger 在 中提供:issue:`35664`。)

  • collections.namedtuple() 中的加速字段查找。 它们现在的速度提高了两倍多,使它们成为 Python 中最快的实例变量查找形式。 (由 Raymond Hettinger、Pablo Galindo 和 Joe Jevnik、Serhiy Storchaka 在 :issue:`32492` 中提供。)

  • 如果输入可迭代对象的长度已知(输入实现 __len__),则 list 构造函数不会过度分配内部项目缓冲区。 这使得创建的列表平均小 12% s。 (由 Raymond Hettinger 和 Pablo Galindo 在 中提供:issue:`33234`。)

  • 将类变量写入的速度加倍。 更新非 dunder 属性时,会不必要地调用更新槽。 (由 Stefan Behnel、Pablo Galindo Salgado、Raymond Hettinger、Neil Schemenauer 和 Serhiy Storchaka 在 :issue:`36012` 中提供。)

  • 减少了转换传递给许多内置函数和方法的参数的开销。 这将调用一些简单的内置函数和方法的速度加快了 20-50%。 (由 Serhiy Storchaka 在 :issue:`23867`:issue:`35582`:issue:`36127` 中贡献。)

  • LOAD_GLOBAL 指令现在使用新的“每个操作码缓存”机制。 现在大约是 40% faster。 (由 Yury Selivanov 和 Inada Naoki 在 中提供:issue:`26219`。)


构建和 C API 更改

  • 默认 sys.abiflags 变为空字符串:pymalloc 的 m 标志变得无用(使用和不使用 pymalloc 的构建都与 ABI 兼容),因此已被删除。 (由 Victor Stinner 在 中提供:问题:`36707`。)

    更改示例:

    • 只安装了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_PY

    • Include/internal/*.h 是 CPython 特有的私有内部 C API。 此 API 没有向后兼容性保证,不应在 CPython 之外使用。 它仅针对非常特定的需求公开,例如调试器和配置文件,它们必须在不调用函数的情况下访问 CPython 内部。 这个 API 现在由 make install 安装。

    (由 Victor Stinner 在 :issue:`35134`:issue:`35081` 中贡献,Eric Snow 在 Python 3.7 中发起的工作。)

  • 一些宏已转换为静态内联函数:参数类型和返回类型定义良好,它们没有特定于宏的问题,变量具有局部作用域。 例子:

    (由 Victor Stinner 在 中提供:问题:`35059`。)

  • PyByteArray_Init()PyByteArray_Fini() 功能已被删除。 自 Python 2.7.4 和 Python 3.2.0 以来,他们什么也没做,被排除在有限的 API(稳定 ABI)之外,并且没有记录。 (由 Victor Stinner 在 中提供:问题:`35713`。)

  • PyExceptionClass_Name() 的结果现在是 const char * 类型而不是 char *。 (由 Serhiy Storchaka 在 中提供:issue:`33818`。)

  • Modules/Setup.distModules/Setup 的二元性已被移除。 以前,在更新 CPython 源代码树时,必须手动将 Modules/Setup.dist(源代码树内部)复制到 Modules/Setup(构建树内部),以反映上游的任何更改。 这对打包人员来说是一个小好处,但代价是在 CPython 开发之后经常给开发人员带来烦恼,因为忘记复制文件可能会导致构建失败。

    现在构建系统总是从源代码树中的 Modules/Setup 读取。 鼓励想要自定义该文件的人在 CPython 的 git fork 或补丁文件中维护他们的更改,就像他们对源树的任何其他更改所做的一样。

    (由 Antoine Pitrou 在 中提供:issue:`32430`。)

  • 将 Python 数字转换为 C 整数的函数(如 PyLong_AsLong())和参数解析函数(如 PyArg_ParseTuple() 与整数转换格式单位(如 'i')现在将使用 ]__index__() 特殊方法而不是 __int__()(如果可用)。 对于具有 __int__() 方法但没有 __index__() 方法的对象(如 DecimalFraction),将发出弃用警告。 PyNumber_Check() 现在将为实现 __index__() 的对象返回 1PyNumber_Long()PyNumber_Float()PyFloat_AsDouble() 现在也使用 __index__() 方法(如果可用)。 (由 Serhiy Storchaka 在 :issue:`36048`:issue:`20092` 中贡献。)

  • 堆分配的类型对象现在将在 PyObject_Init()(及其并行宏 PyObject_INIT)中增加它们的引用计数,而不是在 PyType_GenericAlloc() 中。 可能需要调整修改实例分配或释放的类型。 (由 Eddie Elizondo 在 中提供:issue:`35810`。)

  • 新函数 PyCode_NewWithPosOnlyArgs() 允许创建类似 PyCode_New() 的代码对象,但有一个额外的 posonlyargcount 参数用于指示仅位置参数的数量。 (由 Pablo Galindo 在 中提供:issue:`37221`。)

  • Py_SetPath() 现在将 sys.executable 设置为程序完整路径 (Py_GetProgramFullPath()) 而不是程序名称 (Py_GetProgramName()[ X171X])。 (由 Victor Stinner 在 中提供:问题:`38234`。)


已弃用


API 和功能删除

以下功能和 API 已从 Python 3.8 中删除:

  • 从 Python 3.3 开始,不推荐从 collections 导入 ABC,应从 collections.abc 导入。 能够从集合中导入在 3.8 中被标记为删除,但已延迟到 3.9。 (参见 :issue:`36952`。)
  • Python 3.7 中弃用的 macpath 模块已被删除。 (由 Victor Stinner 在 中提供:问题:`35471`。)
  • 函数 platform.popen() 自 Python 3.3 起被弃用后已被删除:改用 os.popen()。 (由 Victor Stinner 在 中提供:问题:`35345`。)
  • 函数 time.clock() 已被删除,自 Python 3.3 起已被弃用:使用 time.perf_counter()time.process_time() 代替,具体取决于您的要求, 具有明确定义的行为。 (由 Matthias Bussonnier 在 :issue:`36895` 中提供。)
  • pyvenv 脚本已被删除,取而代之的是 python3.8 -m venv,以帮助消除关于 pyvenv 脚本绑定到什么 Python 解释器的混淆。 (由 Brett Cannon 在 中提供:issue:`25427`。)
  • parse_qsparse_qslescapecgi 模块中移除。 它们在 Python 3.2 或更早版本中已被弃用。 它们应该从 urllib.parsehtml 模块导入。
  • filemode 函数从 tarfile 模块中删除。 自 Python 3.3 起没有记录和弃用它。
  • XMLParser 构造函数不再接受 html 参数。 它从来没有效果,在 Python 3.4 中被弃用。 所有其他参数现在都是 仅关键字 。 (由 Serhiy Storchaka 在 中提供:issue:`29209`。)
  • 删除了 XMLParserdoctype() 方法。 (由 Serhiy Storchaka 在 中提供:issue:`29209`。)
  • “unicode_internal”编解码器被删除。 (由 Inada Naoki 在 中提供:issue:`36297`。)
  • sqlite3 模块的 CacheStatement 对象不会暴露给用户。 (由 Aviv Palivoda 在 中提供:issue:`30262`。)
  • fileinput.input()fileinput.FileInput()bufsize 关键字参数自 Python 3.6 以来被忽略和弃用。 :issue:`36952`(由 Matthias Bussonnier 提供。)
  • Python 3.7 中弃用的函数 sys.set_coroutine_wrapper()sys.get_coroutine_wrapper() 已被删除; :issue:`36933`(由 Matthias Bussonnier 提供。)


移植到 Python 3.8

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

Python 行为的变化

  • Yield 表达式(yieldyield from 子句)现在在推导式和生成器表达式中是不允许的(除了最左边的 for 子句中的可迭代表达式)。 (由 Serhiy Storchaka 在 :issue:`10544` 中提供。)
  • 当身份检查(isis not)与某些类型的文字(例如 字符串、数字)。 这些通常可以在 CPython 中意外工作,但语言规范不保证。 该警告建议用户改用相等性测试(==!=)。 (由 Serhiy Storchaka 在 中提供:issue:`34850`。)
  • 在某些情况下,CPython 解释器可以吞下异常。 在 Python 3.8 中,这种情况发生的情况较少。 特别是,不再忽略从类型字典中获取属性时引发的异常。 (由 Serhiy Storchaka 在 中提供:issue:`35459`。)
  • 从内置类型 boolintfloatcomplex 和标准库中的几个类中删除了 __str__ 实现。 它们现在从 object 继承 __str__()。 因此,在这些类的子类中定义 __repr__() 方法会影响它们的字符串表示。 (由 Serhiy Storchaka 在 中提供:issue:`36793`。)
  • 在 AIX 上,sys.platform 不再包含主要版本。 它总是 'aix',而不是 'aix3' .. 'aix7'。 由于较旧的 Python 版本包含版本号,因此建议始终使用 sys.platform.startswith('aix')。 (由 M. 感觉在 :问题:`36588`。)
  • PyEval_AcquireLock()PyEval_AcquireThread() 如果在解释器完成时调用,现在终止当前线程,使它们与 PyEval_RestoreThread(), , 一致()PyGILState_Ensure()。 如果不需要此行为,请通过检查 _Py_IsFinalizing()sys.is_finalizing() 来保护呼叫。 (由 Joannah Nanjekye 在 :issue:`36475` 中提供。)


Python API 的变化

  • 现在更安全地解决了 Windows 上使用 ctypes 加载的扩展模块和 DLL 的 DLL 依赖项。 仅搜索系统路径、包含 DLL 或 PYD 文件的目录以及使用 add_dll_directory() 添加的目录以查找加载时依赖项。 具体来说,不再使用 PATH 和当前工作目录,对这些进行修改将不再对正常的 DLL 分辨率产生任何影响。 如果您的应用程序依赖于这些机制,您应该检查 add_dll_directory() 并且如果它存在,在加载您的库时使用它来添加您的 DLL 目录。 请注意,Windows 7 用户需要确保已安装 Windows Update KB2533623(这也由安装程序验证)。 (由 Steve Dower 在 中提供:issue:`36085`。)
  • 与 pgen 相关的头文件和函数在被纯 Python 实现替换后已被删除。 (由 Pablo Galindo 在 中提供:issue:`36623`。)
  • types.CodeType 在构造函数的第二个位置有一个新参数 (posonlyargcount) 以支持在 PEP 570 中定义的仅位置参数]。 第一个参数 (argcount) 现在表示位置参数的总数(包括仅位置参数)。 types.CodeType 的新 replace() 方法可用于使代码面向未来。


C API 的变化

  • PyCompilerFlags 结构有一个新的 cf_feature_version 字段。 它应该被初始化为PY_MINOR_VERSION。 默认情况下忽略该字段,并且仅当 cf_flags 中设置了 PyCF_ONLY_AST 标志时才使用该字段。 (由 Guido van Rossum 在 中提供:issue:`35766`。)

  • PyEval_ReInitThreads() 函数已从 C API 中删除。 不应显式调用它:改用 PyOS_AfterFork_Child()。 (由 Victor Stinner 在 中提供:问题:`36728`。)

  • 在 Unix 上,除 Android 和 Cygwin 外,C 扩展不再链接到 libpython。 当嵌入 Python 时,libpython 不能用 RTLD_LOCAL 加载,而是用 RTLD_GLOBAL 加载。 以前,使用 RTLD_LOCAL,已经无法加载未链接到 libpython 的 C 扩展,例如由 *shared* 部分构建的标准库的 C 扩展。 X190X]。 (由 Victor Stinner 在 中提供:问题:`21536`。)

  • 在解析或构建值时使用 # 格式的变体(例如 PyArg_ParseTuple(), Py_BuildValue(), PyObject_CallFunction(), etc.) 没有定义 PY_SSIZE_T_CLEAN 现在引发 [X1. 它将在 3.10 或 4.0 中删除。 阅读 解析参数和构建值 了解详细信息。 (由 Inada Naoki 在 中提供:issue:`36381`。)

  • 堆分配类型的实例(例如使用 PyType_FromSpec() 创建的实例)持有对其类型对象的引用。 增加这些类型对象的引用计数已从 PyType_GenericAlloc() 移至更底层的函数,PyObject_Init()PyObject_INIT()。 这使得通过 PyType_FromSpec() 创建的类型的行为类似于托管代码中的其他类。

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

    对于绝大多数情况,应该没有副作用。 但是,在分配实例后手动增加引用计数的类型(可能是为了解决错误)现在可能会变得不朽。 为了避免这种情况,这些类需要在实例释放期间在类型对象上调用 Py_DECREF。

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

    • 分配实例后删除类型对象上的 Py_INCREF - 如果有的话。 这可能在调用 PyObject_New()PyObject_NewVar()PyObject_GC_New()PyObject_GC_NewVar()或任何其他自定义 allocator 后发生使用 PyObject_Init()PyObject_INIT()

      例子:

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

      例子:

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

    (由 Eddie Elizondo 在 中提供:issue:`35810`。)

  • Py_DEPRECATED() 宏已为 MSVC 实现。 宏现在必须放在符号名称之前。

    例子:

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

    (由 Zackery Spytz 在 中提供:issue:`33407`。)

  • 解释器不再假装支持跨功能版本的扩展类型的二进制兼容性。 第三方扩展模块导出的 PyTypeObject 应该具有当前 Python 版本中预期的所有插槽,包括 tp_finalize(不再检查 Py_TPFLAGS_HAVE_FINALIZE在阅读 tp_finalize 之前)。

    (由 Antoine Pitrou 在 中提供:issue:`32388`。)

  • 函数 PyNode_AddChild()PyParser_AddToken() 现在接受两个额外的 int 参数 end_linenoend_col_offset

  • 允许 MinGW 工具直接链接到 python38.dlllibpython38.a 文件不再包含在常规 Windows 发行版中。 如果你需要这个文件,它可以用 gendefdlltool 工具生成,它们是 MinGW binutils 包的一部分:

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

    已安装的 pythonXY.dll 的位置将取决于安装选项以及 Windows 的版本和语言。 有关更多信息,请参阅 在 Windows 上使用 Python 。 生成的库应该和pythonXY.lib放在同一个目录下,一般是你Python安装下的libs目录。

    (由 Steve Dower 在 中提供:issue:`37351`。)


CPython 字节码更改


演示和工具

添加了一个基准脚本,用于对访问变量的各种方式进行计时:Tools/scripts/var_access_benchmark.py。 (由 Raymond Hettinger 在 中提供:issue:`35884`。)

以下是自 Python 3.3 以来的性能改进摘要:

Python version                       3.3     3.4     3.5     3.6     3.7     3.8
--------------                       ---     ---     ---     ---     ---     ---

Variable and attribute read access:
    read_local                       4.0     7.1     7.1     5.4     5.1     3.9
    read_nonlocal                    5.3     7.1     8.1     5.8     5.4     4.4
    read_global                     13.3    15.5    19.0    14.3    13.6     7.6
    read_builtin                    20.0    21.1    21.6    18.5    19.0     7.5
    read_classvar_from_class        20.5    25.6    26.5    20.7    19.5    18.4
    read_classvar_from_instance     18.5    22.8    23.5    18.8    17.1    16.4
    read_instancevar                26.8    32.4    33.1    28.0    26.3    25.4
    read_instancevar_slots          23.7    27.8    31.3    20.8    20.8    20.2
    read_namedtuple                 68.5    73.8    57.5    45.0    46.8    18.4
    read_boundmethod                29.8    37.6    37.9    29.6    26.9    27.7

Variable and attribute write access:
    write_local                      4.6     8.7     9.3     5.5     5.3     4.3
    write_nonlocal                   7.3    10.5    11.1     5.6     5.5     4.7
    write_global                    15.9    19.7    21.2    18.0    18.0    15.8
    write_classvar                  81.9    92.9    96.0   104.6   102.1    39.2
    write_instancevar               36.4    44.6    45.8    40.0    38.9    35.5
    write_instancevar_slots         28.7    35.6    36.1    27.3    26.6    25.7

Data structure read access:
    read_list                       19.2    24.2    24.5    20.8    20.8    19.0
    read_deque                      19.9    24.7    25.5    20.2    20.6    19.8
    read_dict                       19.7    24.3    25.7    22.3    23.0    21.0
    read_strdict                    17.9    22.6    24.3    19.5    21.2    18.9

Data structure write access:
    write_list                      21.2    27.1    28.5    22.5    21.6    20.0
    write_deque                     23.8    28.7    30.1    22.7    21.8    23.5
    write_dict                      25.9    31.4    33.3    29.3    29.2    24.7
    write_strdict                   22.9    28.4    29.9    27.5    25.2    23.1

Stack (or queue) operations:
    list_append_pop                144.2    93.4   112.7    75.4    74.2    50.8
    deque_append_pop                30.4    43.5    57.0    49.4    49.2    42.5
    deque_append_popleft            30.8    43.7    57.3    49.7    49.7    42.8

Timing loop:
    loop_overhead                    0.3     0.5     0.6     0.4     0.3     0.3

基准测试是在运行 macOS 64 位构建的 英特尔® 酷睿™ i7-4960HQ 处理器 上测量的,该处理器位于 python.org。 基准脚本以纳秒为单位显示计时。


Python 3.8.1 中的显着变化

由于重大的安全问题,不再支持 asyncio.loop.create_datagram_endpoint()reuse_address 参数。 这是因为 UDP 中套接字选项 SO_REUSEADDR 的行为。 有关更多详细信息,请参阅 loop.create_datagram_endpoint() 的文档。 (由 Kyle Stanley、Antoine Pitrou 和 Yury Selivanov 在 :issue:`37228` 中提供。)


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 在 :issue:`42967` 中贡献。)


Python 3.8.12 中的显着变化

从 Python 3.8.12 开始,ipaddress 模块不再接受 IPv4 地址字符串中的任何前导零。 前导零是不明确的,一些库将其解释为八进制表示法。 例如,遗留函数 socket.inet_aton() 将前导零视为八进制表示法。 现代 inet_pton() 的 glibc 实现不接受任何前导零。

(最初由 Christian Heimes 在 :issue:`36384` 中贡献,并由 Achraf Merzouki 向后移植到 3.8。)