体验一下 Python 3.8 带来的主要新特性

学习理解一个软件非常好的方法就是跟随每一个版本演进的新特性,好比一个人被别人看着长大的,知子莫若父。因此每个版本的 Changelogs 或 What's New 是非常值得一读的,见 What's New In Python 3.8。因为接触 Python 比较晚,没能即时的重前往后的看过 Python 各版本的新特性,于是采用倒叙的方式来看 Python 的演进也不错。

在正式进入 Python 3.8 新特性前,先来看 Python 3.8 的安装和以及介绍一个非常棒的 Python 命令行解析器 bpython 作为 python 命令,ipython, idle 的替代品。

Python 3.8 各平台的安装包可在 https://www.python.org/downloads/release/python-380/ 找到。比如 Mac OS X 下可下载 https://www.python.org/ftp/python/3.8.0/python-3.8.0-macosx10.9.pkg,然后执行安装。安装完后,我机器上的 /usr/local/bin/python3 就指向到了 Python 3.8, 要用 Python 3.7 的话命令是 python3.7; pip3 却仍然是老版本的。

bpython 的安装要用相应版本的  pip install bpython, 如 /Library/Frameworks/Python.framework/Versions/3.8/bin/pip3 install bpython, 或者创建的虚拟环境中安装, python -m venv .venv; source .venv/bin/activate; pip install bpython。安装后启动  bpython 进入一个更智能的 Python 命令解析器。py

下面正式了解 Python 3.8 的主要新特性

一. 赋值表达式

简单说就是赋值也有返回值的,不只是一个单纯的操作。这是一个 Java 和 C 里的默认行为,也就是 a = 100 这个表达式的返回值是也是 100,所以 Java/C 里可以写成

int a = 50;
int b = (a = 100); 这时候 b 也是 100

也是由于 C 中表达式有返回值以及宽泛的 bool 真值的认定,才有 if(a == 1) 建议写成 if(1 == a)。在 Java 中先前常用的按行读文件的方式

也是因为 Java 的赋值是个表达式。

Python 3.8 之前的版本是不能写成 b = (a=1), 更不能写成 print(a=1) (这不按名传参吗),但是可以用 a = b = 1。

不过介于 Python 等号 = 在传递函数参数时用以指定参数名的用途,所以 Python 创建了 := 组合符号(像海象眼睛和獠牙)来实现赋值表达式,于是在 Python 3.8 中可以写成如下方式

二. 只按位置传递的参数

定义 Python 函数

那么既能用 incr(3.8) 也能用 incr(x=3.8) 两种方式调用。假如我们改写成

那么就只能按位置来传递 x 参数, 用 incr(3.8)

尝试用  incr(x=3.8) 的话会出现如下错误

>>> incr(x=3.8)
Traceback (most recent call last):
File "<input>", line 1, in <module>
incr(x=3.8)
TypeError: incr() got some positional-only arguments passed as keyword arguments: 'x'

定义函数参数中的 / 并不是一个特殊的参数名,它只是用来限定在它之前的参数必须按位置一一匹配参数,不能用参数名来指定。但 / 后的的参数它管不了,像

但下面方式就行了

>> greet(name='world')
Traceback (most recent call last):
File "<input>", line 1, in <module>
greet(name='world')
TypeError: greet() got some positional-only arguments passed as keyword arguments: 'name'

有只能按位置传递参数的方式,同时也有只能按名传递参数的函数,这在 Python 3.7 中就有的特性,就是加一个 * 作为特殊参数名,如

调用时只能通过

来调用,因为星号占据着一个特殊位置,下方是各种错误调用方式的尝试及错误

to_fahrenheit(40)              # TypeError: to_fahrenheit() takes 0 positional arguments but 1 was given
to_fahrenheit(a=3, celsius=40)    # TypeError: to_fahrenheit() got an unexpected keyword argument 'a'

结合一下 /* 在定义函数参数的效果

首先明确一下规则,调用该函数时 / 前的参数必须按位置传入,* 后的参数必须按名传入,而它们之间的参数按名按位置传入都行。所以可用下面的调用

headline("hello", "=", width=20)        # 正确
headline("hello", border="=", width=20)   # 正确
headline(text="hello", border="=", width=20)  # TypeError: headline() got some positional-only arguments passed as keyword arguments: 'text'
headline("hello", border="=", 20)      # SyntaxError: positional argument follows keyword argument

三. 更精确的类型提示

Python 3.7 就开始支持 Type Hints, 对变量,函数参数或函数返回值进行类型提示,如

不知为什么 Python 对类型提示的语法针对参数和函数返回值会用不一样的标识符, :->

注意,这只是类型提示,解释器运行时会把类型提示忽略掉,也就是调是实际类型可以是任意的。像下面的代码是合法的

但这种类型提示可被 IDE 利用或是用第三方工具来进行静态检查,比如 mypy(用 pip install mypy 安装). 因为既然用了类型提示就该严格遵循代码的意愿。

上面是 Python 3.7 已支持的特性,现在看 Python 3.8 加入的一些 Literal type 类型提示,同样它也不是语法强制,而是像 mypy 来进行静态检查时的约束。下面来看一堆例子

Literal 是限定值的选择,此外还有 Union 类型的提示 -- 可接受类型的枚举

还有 Final 或 @final 装饰,相当于 Java 的 final 关键字,变量不能重赋值,类不能被继承,方法不能被覆盖

TypeDict: 像是 NamedTuple
from typing import TypedDict    //不作具体介绍
 
Protocol:  Duck Typing, 实例含有的特定方法比类型更重要, Protocol 像是一个标识接口
from typing import Protocol     //也不作具体介绍,好像没多大意义

四. 用 f-strings 进行简单的调试

f-strings 是 Python 3.6 开始引入的语法,用于字符串内的表达式解析,如

f"Diameter {(diam :=2 * r)} give circumference {math.pi * diam:.2f}" #可以运算,中间变量,格式化

在 Python 3.8 中的 f-strings 能力有所增强,f-strings 里 {} 中变量名后再加个等能同时显示变量名与值。

五. 新模块 importlib.metadata

可以探查安装了什么包

六. Math 和 Statistics 函数 (NumPy 可能会是更好的选择)

math.prod([2, 3, 4])      返回 24, 即 2 * 3 * 4
math.isqrt(15)     //开方的整数部分
math.dist((16, 25, 20), (8, 15, 14))  // 返回 M 维空间两点间的距离 14.142135623730951
math.hypot(16, 25, 20)  //返回 M 维坐标原点与某个点之间的距离 35.79106033634656
 
import statistics
statistics.fmean([9, 3, 2])  //浮点数的平均值  4.666666666666667
statistics.geometric_mean([9, 3, 2]) // 几何平均数  3.77976314968462
statistics.multimode(), statistics.quantiles()
statistics.NormalDist //用于 Gaussian normal distribution 高斯正态分布
statsmodels, scipy.stats

七. 警告信息, 更聪明一些, 以下红色部分是新加的提示信息

>>> version = "3.8"
>>> version is "3.8"
<bpython-input-17>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
  version is "3.8"
在 Python 3.8 之前的版本没有警告,直接返回 False
 
>>> [(3,5) (6,8)]
TypeError: 'tuple' object is not callable
>>>
<input>:2: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
sizeof
>>>import sys
>>>sys.getsizeof(list(range(20191014))  //内存使用 sizeof
161528168

类别: Python. 标签: . 阅读(51). 订阅评论. TrackBack.
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x