2019.10.15日python 3.8.0 稳定版正式发布,让我们来看看究竟有什么新特性和优化吧。
Python 3.8.0 稳定版 部分新特性:
PEP 572: Assignment Expressions
PEP 572 的标题是复制表达式,也叫做 [命名表达式],不过他现在亲切的被称为"海象运算符"(the walrus operator)。因为它长得就像海象的眼睛和牙齿 := 2333…
赋值表达式有很多用处,避免重复调用,减少正则匹配次数,while循环里再次使用,列表解析式里的应用等等…贴几个官方的代码:
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
# 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]
Try to limit use of the walrus operator to clean cases that reduce complexity and improve readability.
注意,要有限制的使用海象运算符,要减少复杂性,提高可读性。
PEP 570: Positional-only parameters
PEP 570 就是曾经缺少的那种传参方式——Positional-only parameters 仅位置参数
是一种新的函数参数语法, /
,表明一些函数参数必须指定位置,不能用作关键字参数。让我们看几个官网例子:
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不能作为关键字参数传参
f(10, 20, 30, 40, 50, f=60) # e 必须是关键字传参
这种表示法的一个用例是,它允许纯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
另一个用例是在参数名没有帮助时排除关键字参数。例如,内奸 len() 函数有一个签名len (obj, /)。这就避免了一些尴尬的调用,比如:
len(obj='hello') # The "obj" keyword agrument impairs readability
将参数标记为 positional-only 的另一个好处是,它允许在将来更改参数名称,而不会有破坏客户机代码的风险。例如,在统计模块中,参数名 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 和 b 用两种方式传入
-----
10 20 {'a': 1, 'b': 2, 'c': 3}
这极大地简化了需要接受任意关键字参数的函数和方法的实现。例如,以下是collections模块中的代码摘录:
class Counter(dict):
def __init__(self, iterable=None, /, **kwargs):
# Note "iterable" is a possible keyword argument
PEP 578: Python Runtime Audit Hooks
现在可以给 Python 运行时添加审计钩子:
import sys
import urllib.request
def audit_hook(event, args):
if event in ['urllib.Request']:
print(f'Network {event=} {args=}')
sys.addaudithook(audit_hook)
In: urllib.request.urlopen('https://httpbin.org/get?a=1')
Network event='urllib.Request' args=('https://httpbin.org/get?a=1', None, {}, 'GET')
Out: <http.client.HTTPRespinse at 0x10e304310>
目前支持审计的时间名字和 API 可以看 PEP 文档,urllib.Request 是其中之一。另外还可以自定义事件:
def audit_hook(event, args):
if event in ['make_request']:
print(f'Network {event=} {args=}')
In: sys.addaudithook(audit_hook)
In: sys.audit('make_request', 'https://baidu.com')
Network event = 'make_request' args=('https://baidu.com',)
In: sys.audit('make_request', 'https://douban.com')
Network event='make_request' args=('https://douban,com',)
Multiprocessing shared memory
可以跨进程直接访问同一内存(共享):
# IPython 进程A
In : from multiprocessing import shared_memory
In : a = shared_memory.ShareableList([1, 'a', 0.1])
In : a
Out : ShareableList([1, 'a', 0.1], name='psm_d5d6ba1b') # 注意name
# IPython进程B(另外一个终端进入IPython)
In : from multiprocessing import shared_memory
In : b = shared_memory.ShareableList(name='psm_d5d6ba1b')
In : b
Out : ShareableList([1, 'a', 0.1], name='psm_d5d6ba1b')
New importlib.metadata module
使用新的 importlib.metadata 模块可以直接读取第三方包的元数据:
In : from importlib.metadata import version, files, requires, distribution
In : version('flask')
Out : '1.1.1'
In : requires('requests')
Out :
['chardet (<3.1.0,>=3.0.2)',
'idna (<2.9,>=2.5)',
'urllib3 (!=1.25.0,!=1.25.1,<1.26,>=1.21.1)',
'certifi (>=2017.4.17)',
"pyOpenSSL (>=0.14) ; extra == 'security'",
"cryptography (>=1.3.4) ; extra == 'security'",
"idna (>=2.0.0) ; extra == 'security'",
"PySocks (!=1.5.7,>=1.5.6) ; extra == 'socks'",
'win-inet-pton ; (sys_platform == "win32" and python_version == "2.7") and extra == \'socks\'']
In : dist = distribution('celery')
In : dist.version
Out: '4.3.0'
In : dist.metadata['Requires-Python']
Out: '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
In : dist.metadata['License']
In : dist.entry_points
Out:
[EntryPoint(name='celery', value='celery.__main__:main', group='console_scripts'),
EntryPoint(name='celery', value='celery.contrib.pytest', group='pytest11')]
In : files('celery')[8]
Out: PackagePath('celery/__init__.py')
In : dist.locate_file(files('celery')[8])
Out: PosixPath('/Users/dongweiming/test/venv/lib/python3.8/site-packages/celery/__init__.py')
functools.cached_property
缓存属性 (cached_property) 是一个非常常用的功能,很多知名 Python 项目都自己实现过它,现在终于进入版本库了。
未完待续…