北京时间 10 月 15 日,Python 官方发布了 3.8.0 正式版,该版本较 3.7 版本再次带来了多个非常实用的新特性。
赋值表达式 {#赋值表达式}
PEP 572: Assignment Expressions
新增一种新语法形式::=
,又称为"海象运算符"(为什么叫海象,看看这两个符号像不像颜表情),如果你用过 Go 语言,应该对这个语法非常熟悉。
具体作用我们直接用实例来展示,比如在使用正则匹配时,以往版本中我们会如下写:
|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| hljs python import re pattern = re.compile('a') data = 'abc' match = pattern.search(data) if match is not None: print(match.group(0))
|
而使用赋值表达式时,我们可以改写为:
|-------------|------------------------------------------------------------------------------------------|
| 1 2
| hljs python if (match := pattern.search(data)) is not None: print(match.group(0))
|
在 if 语句中同时完成了求值、赋值变量、变量判断三步操作,再次简化了代码。
下面是在列表表达式中的用法:
|-----------|------------------------------------------------------------------------------------|
| 1
| hljs python filtered_data = [y for x in data if (y := func(x)) is not None]
|
强制位置参数 {#强制位置参数}
PEP 570: Python Positional-Only parameters
新增一个函数形参标记:/
,用来表示标记左侧的参数,都只接受位置参数,不能使用关键字参数形式。
|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10
| hljs python >>> def pow(x, y, z=None, /): ... r = x ** y ... return r if z is None else r%z ... >>> pow(5, 3) 125 >>> pow(x=5, y=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pow() takes no keyword arguments
|
这实际上是用纯 Python 代码来模拟现有 C 代码实现的内置函数中类似功能,比如内置函数 len('string')
传参是不能使用关键字参数的。
Runtime 审计钩子 {#runtime-审计钩子}
PEP 578: Python Runtime Audit Hooks
这让我们可以对某些事件和 API 添加一些钩子,用于在运行时监听事件相关的参数。
比如这里监听 urllib 请求:
|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10
| hljs python >>> import sys >>> import urllib.request >>> def audit_hook(event, args): ... if event in ['urllib.Request']: ... print(f'{event=} {args=}') ... >>> sys.addaudithook(audit_hook) >>> urllib.request.urlopen('https://httpbin.org/get?a=1') event = 'urllib.Request' args =( 'https://httpbin.org/get?a=1' , None , {}, 'GET' ) <http.client.HTTPResponse object at 0x108f09b38>
|
官方内置了一些 API,具体可查看 PEP-578 规范文档,也可以自定义。
f-strings 支持等号 {#f-strings-支持等号}
在 Python 3.6 版本中增加了 f-strings,可以使用 f 前缀更方便地格式化字符串,同时还能进行计算,比如:
|---------------|------------------------------------------------------|
| 1 2 3
| hljs python >>> x = 10 >>> print(f'{x+1}') 11
|
在 3.8 中只需要增加一个 =
符号,即可拼接运算表达式与结果:
|---------------|-------------------------------------------------------------|
| 1 2 3
| hljs python >>> x = 10 >>> print(f'{x+1=}') 'x+1=11'
|
这个特性官方指明了适用于 Debug。
Asyncio 异步交互模式 {#asyncio-异步交互模式}
在之前版本的 Python 交互模式中(REPL),涉及到 Asyncio 异步函数,通常需要使用 asyncio.run(func())
才能执行。
而在 3.8 版本中,当使用 python -m asyncio
进入交互模式,则不再需要 asyncio.run
。
|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| hljs python >>> import asyncio >>> async def test(): ... await asyncio.sleep(1) ... return 'test' ... >>> await test() 'test'
|
跨进程共享内存 {#跨进程共享内存}
在 Python 多进程中,不同进程之间的通信是常见的问题,通常的方式是使用 multiprocessing.Queue
或者 multiprocessing.Pipe
,在 3.8 版本中加入了 multiprocessing.shared_memory
,利用专用于共享 Python 基础对象的内存区域,为进程通信提供一个新的选择。
|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| hljs python from multiprocessing import Process from multiprocessing import shared_memory share_nums = shared_memory.ShareableList(range(5)) def work1(nums): for i in range(5): nums[i] += 10 print('work1 nums = %s'% nums) def work2(nums): print('work2 nums = %s'% nums) if __name__ == '__main__': p1 = Process(target=work1, args=(share_nums, )) p1.start() p1.join() p2 = Process(target=work2, args=(share_nums, )) p2.start() # 输出结果: # work1 nums = [10, 11, 12, 13, 14] # work2 nums = [10, 11, 12, 13, 14]
|
以上代码中 work1 与 work2 虽然运行在两个进程中,但都可以访问和修改同一个 ShareableList
对象。
@cached_property {#cached-property}
熟悉 Python Web 开发的同学,对 werkzeug.utils.cached_property
与 django.utils.functional.cached_property
这两个装饰器一定非常熟悉,它们是内置 @property
装饰器的加强版,被装饰的实例方法不仅变成了属性调用,还会自动缓存方法的返回值。
现在官方终于加入了自己的实现:
|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| hljs python >>> import time >>> from functools import cached_property >>> class Example: ... @cached_property ... def result(self): ... time.sleep(1) # 模拟计算耗时 ... print('work 1 sec...') ... return 10 ... >>> e = Example() >>> e.result work 1 sec... 10 >>> e.result # 第二次调用直接使用缓存,不会耗时 10
|
其他改进 {#其他改进}
- PEP 587: Python 初始化配置
- PEP 590: Vectorcall,用于 CPython 的快速调用协议
finally:
中现在允许使用continue
typed_ast
被合并回 CPythonpickle
现在默认使用协议4,提高了性能LOAD_GLOBAL
速度加快了 40%unittest
加入了异步支持- 在 Windows 上,默认 asyncio 事件循环现在是
ProactorEventLoop
- 在 macOS 上,
multiprocessing
启动方法默认使用spawn
更多具体变化,可查看 What's New In Python 3.8