官宣:Python 3.8来啦!新版本的强大功能,你知道吗?

官宣:Python 3.8来啦!新版本的强大功能,你知道吗?

就在昨天Python官网悄悄发布一则消息,Python3.8来了……3.9正在开发中。想知道Python3.8和Python3.7的区别吗?仔细看,别眨眼↓↓↓

一.新功能

1. 赋值表达式

There is new syntax := that assigns values to variables as part of a larger expression. It is affectionately known as “the walrus operator” due to its resemblance to the eyes and tusks of a walrus.有一种新语法:=将值赋给变量,作为更大表达式的一部分。

In this example, the assignment expression helps avoid calling len() twice: 在本例中,赋值表达式有助于避免调用len()两次:

if (n := len(a)) > 10:

print(f"List is too long ({n} elements, expected <= 10)")

A similar benefit arises during regular expression matching where match objects are needed twice, once to test whether a match occurred and another to extract a subgroup: 在正则表达式匹配期间也有类似的好处,其中需要两次匹配对象,一次用于测试匹配是否发生,另一次用于提取子组:

discount = 0.0

if (mo := re.search(r'(\d+)% discount', advertisement)):

discount = float(mo.group(1)) / 100.0

The operator is also useful with while-loops that compute a value to test loop termination and then need that same value again in the body of the loop: 运算符对于while-loops也很有用,while-loops计算一个值来测试循环终止,然后在循环体中再次需要相同的值:

# Loop over fixed length blocks

while (block := f.read(256)) != '':

process(block)

Another motivating use case arises in list comprehensions where a value computed in a filtering condition is also needed in the expression body: 另一个激励性用例出现在列表理解中,其中表达式体中还需要在过滤条件下计算的值:

[clean_name.title() for name in names

if (clean_name := normalize('NFC', name)) in allowed_names]

Try to limit use of the walrus operator to clean cases that reduce complexity and improve readability. 尝试限制使用海象操作符来清理案件,以降低复杂性并提高可读性。

2. 仅限位置参数

There is a new function parameter syntax / to indicate that some function parameters must be specified positionally and cannot be used as keyword arguments. This is the same notation shown by help() for C functions annotated with Larry Hastings’ Argument Clinic tool. 有一个新的函数参数语法/表示某些函数参数必须在位置上指定,不能用作关键字参数。对于用larry hastings的参数诊所工具注释的c函数,help()显示了相同的符号。

In the following example, parameters a and b are positional-only, while c or d can be positional or keyword, and e or f are required to be keywords: 在以下示例中,参数a和b仅是位置性的,而c或d可以是位置性的或关键字,e或f需要是关键字:

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

print(a, b, c, d, e, f)

The following is a valid call: 以下是有效的呼叫:

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

However, these are invalid calls: 但是,这些都是无效呼叫:

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

One use case for this notation is that it allows pure Python functions to fully emulate behaviors of existing C coded functions. For example, the built-in pow() function does not accept keyword arguments: 这个符号的一个用例是,它允许纯Python函数完全模拟现有c代码函数的行为。例如,内置的pow()函数不接受关键字参数:

def pow(x, y, z=None, /):

"Emulate the built in pow() function"

r = x ** y

return r if z is None else r%z

Another use case is to preclude keyword arguments when the parameter name is not helpful. For example, the builtin len() function has the signature len(obj, /). This precludes awkward calls such as: 另一个用例是在参数名没有帮助时排除关键字参数。例如,内置len()函数具有签名len(obj,/)。这排除了尴尬的呼叫,例如:

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

A further benefit of marking a parameter as positional-only is that it allows the parameter name to be changed in the future without risk of breaking client code. For example, in the statistics module, the parameter name dist may be changed in the future. This was made possible with the following function specification: 将参数标记为仅定位的另一个好处是,它允许参数名称在将来更改,而不会有破坏客户端代码的风险。例如,在统计模块中,参数名称dist将来可能会更改。这是通过以下功能规范实现的:

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

...

Since the parameters to the left of / are not exposed as possible keywords, the parameters names remain available for use in **kwargs: 由于/左边的参数没有作为可能的关键字公开,参数名称仍然可以在**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}

This greatly simplifies the implementation of functions and methods that need to accept arbitrary keyword arguments. For example, here is an excerpt from code in the collections module: 这大大简化了需要接受任意关键字参数的函数和方法的实现。例如,下面是收集模块中代码的摘录:

class Counter(dict):

def __init__(self, iterable=None, /, **kwds):

# Note "iterable" is a possible keyword argument

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

The new PYTHONPYCACHEPREFIX setting (also available as -X pycache_prefix) configures the implicit bytecode cache to use a separate parallel filesystem tree, rather than the default __pycache__ subdirectories within each source directory. 新的PYTHONPYCACHEPREFIX设置(也可作为-x pycache_prefix使用)将隐式字节码缓存配置为使用单独的并行文件系统树,而不是每个源目录中的默认__pycache__子目录。

The location of the cache is reported in sys.pycache_prefix (None indicates the default location in __pycache__ subdirectories). 缓存的位置在sys.pycache_prefix中报告(无表示__pycache__子目录中的默认位置)。

4. 调试版本使用与发布版本相同的ABI

Python now uses the same ABI whether it’s built in release or debug mode. On Unix, when Python is built in debug mode, it is now possible to load C extensions built in release mode and C extensions built using the stable ABI. Python现在使用相同的abi,无论它是在发布模式还是调试模式下构建的。在unix上,当Python在调试模式下构建时,现在可以加载在发布模式下构建的c扩展和使用稳定abi构建的c扩展。

Release builds and debug builds are now ABI compatible: defining the Py_DEBUG macro no longer implies the Py_TRACE_REFS macro, which introduces the only ABI incompatibility. The Py_TRACE_REFS macro, which adds the sys.getobjects() function and the PYTHONDUMPREFS environment variable, can be set using the new ./configure --with-trace-refs build option. (Contributed by Victor Stinner in bpo-36465.) 发布版本和调试版本现在是ABI兼容的:定义py_debug宏不再意味着py_trace_refs宏,它引入了唯一的ABI不兼容。py_trace_refs宏添加sys.getobjects()函数和pythondumprefs环境变量,可以使用new./configure--with trace refs build选项进行设置。

On Unix, C extensions are no longer linked to libpython except on Android and Cygwin. It is now possible for a statically linked Python to load a C extension built using a shared library Python. (Contributed by Victor Stinner in bpo-21536.) 在Unix上,除了android和cygwin之外,c扩展不再链接到libpython。现在,静态链接的Python可以加载使用共享库Python构建的c扩展。

On Unix, when Python is built in debug mode, import now also looks for C extensions compiled in release mode and for C extensions compiled with the stable ABI. (Contributed by Victor Stinner in bpo-36722.) 在Unix上,当Python在调试模式下构建时,import现在还查找在发布模式下编译的c扩展,以及用稳定的abi编译的c扩展。

To embed Python into an application, a new --embed option must be passed to python3-config --libs --embed to get -lpython3.8 (link the application to libpython). To support both 3.8 and older, try python3-config --libs --embed first and fallback to python3-config --libs (without --embed) if the previous command fails. 要将python嵌入到应用程序中,必须向python3 config--libs--embed传递一个新的--embed选项,以获取-lpython3.8(将应用程序链接到libpython)。若要同时支持3.8和更高版本,请尝试python3 config--libs--embed first,如果前面的命令失败,请回退到python3 config--libs(不带--embed)。

Add a pkg-config python-3.8-embed module to embed Python into an application: pkg-config python-3.8-embed --libs includes -lpython3.8. To support both 3.8 and older, try pkg-config python-X.Y-embed --libs first and fallback to pkg-config python-X.Y --libs (without --embed) if the previous command fails (replace X.Y with the Python version). 添加pkg config python-3.8-embed模块将Python嵌入到应用程序中:pkg config python-3.8-embed--libs includes-lpython3.8。要同时支持3.8及更高版本,请尝试pkg config python-x.y-embed--libs first,如果前面的命令失败(用python版本替换x.y),则返回pkg config python-x.y--libs(不带--embed)。

On the other hand, pkg-config python3.8 --libs no longer contains -lpython3.8. C extensions must not be linked to libpython (except on Android and Cygwin, whose cases are handled by the script); this change is backward incompatible on purpose. 另一方面,pkg config Python3.8--libs不再包含-lpython3.8。c扩展不能链接到libpython(android和cygwin除外,它们的情况由脚本处理);这种更改是故意向后不兼容的。

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

Added an = specifier to f-strings. An f-string such as f'{expr=}' will expand to the text of the expression, an equal sign, then the representation of the evaluated expression. For example: 向f字符串添加了=说明符。像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)"

The usual f-string format specifiers allow more control over how the result of the expression is displayed: 通常的f字符串格式说明符允许更多地控制表达式结果的显示方式:

>>> delta = date.today() - member_since

>>> f'{user=!s} {delta.days=:,d}'

'user=eric_idle delta.days=16,075'

The = specifier will display the whole expression so that calculations can be shown: =说明符将显示整个表达式,以便显示计算结果:

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

theta=30 cos(radians(theta))=0.866

6. PEP 578:Python运行时审计挂钩

The PEP adds an Audit Hook and Verified Open Hook. Both are available from Python and native code, allowing applications and frameworks written in pure Python code to take advantage of extra notifications, while also allowing embedders or system administrators to deploy builds of Python where auditing is always enabled. PEP添加一个审计钩子和已验证的开放钩子。两者都可以从Python和本机代码中获得,允许使用纯Python代码编写的应用程序和框架利用额外的通知,同时还允许嵌入程序或系统管理员在始终启用审计的情况下部署Python构建。

7. PEP 587:Python初始化配置

The PEP 587 adds a new C API to configure the Python Initialization providing finer control on the whole configuration and better error reporting. PEP 587添加了一个新的api来配置Python初始化,从而对整个配置提供更好的控制和更好的错误报告。

New structures:

• PyConfig

• PyPreConfig

• PyStatus

• PyWideStringList

New functions:

• PyConfig_Clear()

• PyConfig_InitIsolatedConfig()

• PyConfig_InitPythonConfig()

• PyConfig_Read()

• PyConfig_SetArgv()

• PyConfig_SetBytesArgv()

• PyConfig_SetBytesString()

……

This PEP also adds _PyRuntimeState.preconfig (PyPreConfig type) and PyInterpreterState.config (PyConfig type) fields to these internal structures. PyInterpreterState.config becomes the new reference configuration, replacing global configuration variables and other private variables. PEP还将pyruntimestate.prefig(pyprefig type)和pyinterpreterstate.config(pyconfig type)字段添加到这些内部结构中。pyinterpreterstate.config将成为新的引用配置,替换全局配置变量和其他私有变量。

8. Vectorcall:一种CPython的快速调用协议

The “vectorcall” protocol is added to the Python/C API. It is meant to formalize existing optimizations which were already done for various classes. Any extension type implementing a callable can use this protocol. “Vectorcall”协议被添加到Python/C API中。这意味着将现有的优化形式化为已经完成的各种类。任何实现可调用的扩展类型都可以使用此协议。

This is currently provisional, the aim is to make it fully public in Python 3.9. 这是暂时的,目的是让它在Python3.9中完全公开。

9. 外数据缓冲区的pickle协议5

When pickle is used to transfer large data between Python processes in order to take advantage of multi-core or multi-machine processing, it is important to optimize the transfer by reducing memory copies, and possibly by applying custom techniques such as data-dependent compression. 当pickle用于在Python进程之间传输大数据以利用多核或多机处理时,通过减少内存副本,并可能通过应用自定义技术(如数据相关压缩)来优化传输非常重要。

The pickle protocol 5 introduces support for out-of-band buffers where PEP 3118-compatible data can be transmitted separately from the main pickle stream, at the discretion of the communication layer. pickle协议5引入了对带外缓冲区的支持,其中pep 3118兼容的数据可以根据通信层的判断从主pickle流中单独传输。

其他语言变化

A continue statement was illegal in the finally clause due to a problem with the implementation. In Python 3.8 this restriction was lifted. 由于实现中的问题,finally子句中的continue语句是非法的。在Python 3.8中,这个限制被取消了。

The bool, int, and fractions.Fraction types now have an as_integer_ratio() method like that found in float and decimal.Decimal. This minor API extension makes it possible to write numerator, denominator = x.as_integer_ratio() and have it work across multiple numeric types. bool、int和fractions.fraction类型现在有一个as_integer_ratio()方法,类似于float和decimal.decimal中的方法。这个小的api扩展使得可以编写分子,分母=x.as_integer_ratio(),并让它跨多个数字类型工作。

Constructors of int, float and complex will now use the __index__() special method, if available and the corresponding method __int__(), __float__() or __complex__() is not available. int、float和complex的构造函数现在将使用uuu index_uuu()特殊方法(如果可用),而相应的方法uuu int_uuu()、uuu float_uuu()或uuu complex_uuu()不可用。

Added support of \N{name} escapes in regular expressions: 在正则表达式中添加了对转义的支持:

>>> notice = 'Copyright © 2019'

>>> copyright_year_pattern = re.compile(r'\N{copyright sign}\s*(\d{4})')

>>> int(copyright_year_pattern.search(notice).group(1))

2019

Dict and dictviews are now iterable in reversed insertion order using reversed(). dict和dictviews现在可以使用reversed()以相反的插入顺序访问。

The syntax allowed for keyword names in function calls was further restricted. In particular, f((keyword)=arg) is no longer allowed. It was never intended to permit more than a bare name on the left-hand side of a keyword argument assignment term. 函数调用中关键字名称所允许的语法进一步受到限制。特别是,f((keyword)=arg)不再被允许。它从未打算在关键字参数赋值项的左侧允许超过一个裸名称。

Generalized iterable unpacking in yield and return statements no longer requires enclosing parentheses. This brings the yield and return syntax into better agreement with normal assignment syntax: yield和return语句中的广义iterable解包不再需要括括号。这使得yield和return语法与普通赋值语法更加一致:

>>> def parse(family):

lastname, *members = family.split()

return lastname.upper(), *members

>>> parse('simpsons homer marge bart lisa sally')

('SIMPSONS', 'homer', 'marge', 'bart', 'lisa', 'sally')

When a comma is missed in code such as [(10, 20) (30, 40)], the compiler displays a SyntaxWarning with a helpful suggestion. This improves on just having a TypeError indicating that the first tuple was not callable. 当代码(如[(10,20)(30,40)]中缺少逗号时,编译器会显示一个syntaxwarning,并给出一个有用的建议。这比仅仅有一个表示第一个元组不可调用的typeerror有所改进。

Arithmetic operations between subclasses of datetime.date or datetime.datetime and datetime.timedelta objects now return an instance of the subclass, rather than the base class. This also affects the return type of operations whose implementation (directly or indirectly) uses datetime.timedelta arithmetic, such as datetime.datetime.astimezone(). DateTime.Date或DateTime.DateTime的子类与DateTime.TimeDelta对象之间的算术运算现在返回子类的实例,而不是基类。这也会影响其实现(直接或间接)使用datetime.timedelta算法(如datetime.datetime.astimezone())的操作的返回类型。

When the Python interpreter is interrupted by Ctrl-C (SIGINT) and the resulting KeyboardInterrupt exception is not caught, the Python process now exits via a SIGINT signal or with the correct exit code such that the calling process can detect that it died due to a Ctrl-C. Shells on POSIX and Windows use this to properly terminate scripts in interactive sessions. 当Python解释器被ctrl-c(sigint)中断时,结果键盘中断异常未被捕获,Python进程现在通过SIGINT信号或正确的退出代码退出,这样调用进程就可以检测到它是由于CCTL—C在Posix和Windows上的外壳而死亡的,它使用它来正确地终止交互会话中的脚本。

Some advanced styles of programming require updating the types.CodeType object for an existing function. Since code objects are immutable, a new code object needs to be created, one that is modeled on the existing code object. With 19 parameters, this was somewhat tedious. Now, the new replace() method makes it possible to create a clone with a few altered parameters. 某些高级编程风格需要为现有函数更新Type。由于代码对象是不可变的,需要创建一个新代码对象,该代码对象是在现有代码对象上建模的。有19个参数,这有点乏味。现在,新的replace()方法使创建一个带有一些修改过的参数的克隆成为可能。

Here’s an example that alters the statistics.mean() function to prevent the data parameter from being used as a keyword argument: 下面是一个修改statistics.mean()函数以防止数据参数用作关键字参数的示例:

>>> 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'

For integers, the three-argument form of the pow() function now permits the exponent to be negative in the case where the base is relatively prime to the modulus. It then computes a modular inverse to the base when the exponent is -1, and a suitable power of that inverse for other negative exponents. For example, to compute the modular multiplicative inverse of 38 modulo 137, write: 对于整数,pow()函数的三参数形式现在允许指数为负,前提是基相对于模的素数。然后,当指数为-1时,它计算出基的模逆,并为其他负指数计算出该逆的适当幂。例如,要计算38模137的模乘逆,请写入:

>>> pow(38, -1, 137)

119

>>> 119 * 38 % 137

1

Modular inverses arise in the solution of linear Diophantine equations. For example, to find integer solutions for 4258� + 147� = 369, first rewrite as 4258� ≡ 369 (mod 147) then solve: 在解线性丢番图方程时出现模逆。例如,要找到4258+147=369的整数解,首先重写为4258� ≡ 369 (mod 147),然后求解:

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

>>> y = (4258 * x - 369) // -147

>>> 4258 * x + 147 * y

369

Dict comprehensions have been synced-up with dict literals so that the key is computed first and the value second: dict comprehension已与dict literals同步,以便首先计算键,然后计算值:

>>> # 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

The guaranteed execution order is helpful with assignment expressions because variables assigned in the key expression will be available in the value expression: 保证的执行顺序有助于赋值表达式,因为键表达式中赋值的变量将在值表达式中可用:

>>> 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'}

新模块

The new importlib.metadata module provides (provisional) support for reading metadata from third-party packages. For example, it can extract an installed package’s version number, list of entry points, and more: 新的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')]

改进模块

1. ast

AST nodes now have end_lineno and end_col_offset attributes, which give the precise location of the end of the node. (This only applies to nodes that have lineno and col_offset attributes.) ast节点现在具有end_lineno和end_col_offset属性,这些属性提供了节点结束的精确位置。(这仅适用于具有lineno和col_offset属性的节点。)

2. asyncio

Running python -m asyncio launches a natively async REPL. This allows rapid experimentation with code that has a top-level await. There is no longer a need to directly call asyncio.run() which would spawn a new event loop on every invocation: 运行python-m asyncio将启动本机异步repl。这可以快速试验具有顶级相关的代码。不再需要直接调用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

3. builtins

The compile() built-in has been improved to accept the ast.PyCF_ALLOW_TOP_LEVEL_AWAIT flag. With this new flag passed, compile() will allow top-level await, async for and async with constructs that are usually considered invalid syntax. Asynchronous code object marked with the CO_COROUTINE flag may then be returned. compile()内置程序已经改进,可以接受ast.pycf_allow_top_level_await标志。传递新标志后,compile()将允许顶级await、async for和async with构造,这些构造通常被视为无效语法。然后可以返回标记有co_coroutine标志的异步代码对象。

4. collections

The _asdict() method for collections.namedtuple() now returns a dict instead of a collections.OrderedDict. This works because regular dicts have guaranteed ordering since Python 3.7. If the extra features of OrderedDict are required, the suggested remediation is to cast the result to the desired type: OrderedDict(nt._asdict()). collections.namedtuple()的_asdict()方法现在返回一个dict,而不是collections.orderedict。这是因为自Python 3.7以来,常规dict保证了排序。如果需要ordereddict的额外特性,建议的补救方法是将结果转换为所需的类型:ordereddict(nt.\\u asdict())。

5. curses

Added a new variable holding structured version information for the underlying ncurses library: ncurses_version. 为基础ncurses库添加了一个包含结构化版本信息的新变量:ncurses_version。

……

相关推荐