python3.8究竟更新了什么?

2019.10.15日python 3.8.0 稳定版正式发布,让我们来看看究竟有什么新特性和优化吧。


Python 3.8.0 稳定版 部分新特性:

  1. PEP 572 ,赋值表达式
  2. PEP 570 ,仅位置参数
  3. PEP 587 ,Python 初始化配置(改进嵌入)
  4. PEP 590 ,Vectorcall:用于 CPython 的快速调用协议
  5. PEP 578 ,运行时审核挂钩
  6. PEP 574 ,带外数据的 Pickle 协议5
  7. Typing 相关(Typing-related):PEP 591,PEP 586 和 PEP 589
  8. 并行文件系统缓存,用于编译的字节码
  9. 调试版本与发行版本共享 ABI
  10. typed_ast 被合并回 CPython
  11. LOAD_GLOBAL 现在快 40%
  12. pickle 现在默认使用协议4,提高了性能

那么让我们再细一点看一看

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 项目都自己实现过它,现在终于进入版本库了。

未完待续…

你可能感兴趣的:(日常)