Python 3.10 的发布日益临近,是时候来看看它将带来的最重要的新特性和变化了。内容包括类型检查,类型别名,switch/case语法,数量统计,上下文管理器,性能等。
作者:Martin Heinz 编译:McGL
每年的这个时候,最新的 Python alpha 版本陆续发布(昨天刚发布了 alpha 第七版),第一个 beta 版即将推出(预计下个月3号),所以现在正是试用 Python 新版本的理想时机,看看 Python 3.10中有哪些酷炫的新功能即将推出!
如果想尝试最新和最好版本的 Python 的所有特性,你需要安装 Alpha/Beta 版本。但是考虑到这个版本还不稳定,我们不希望用它覆盖默认的 Python 安装。所以要保留现有的解释器同时安装 Python 3.10,我们可以使用以下方法:
wget https://www.python.org/ftp/python/3.10.0/Python-3.10.0a7.tgz
tar xzvf Python-3.10.0a7.tgz
cd Python-3.10.0a7
./configure --prefix=$HOME/python-3.10.0a7
make
make install
$HOME/python-3.10.0a7/bin/python3.10
在运行上面的代码之后,你会看到 Python 3.10 Alpha IDLE:
Python 3.10.0a7 (default, Apr 16 2021, 11:50:33) [GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
安装了 Python 3.10 之后,我们来看看所有的新特性和变化......
如果你在 Python 中使用过类型检查,会很高兴看到 Python 3.10 将包括很多类型检查改进,包括语法更清晰的 类型 Union 运算符:
# Function that accepts either `int` or `float`
# Old:
def func(value: Union[int, float]) -> Union[int, float]:
return value
# New:
def func(value: int | float) -> int | float:
return value
除此之外,这个简单的改进不仅限于类型注释,还可以用于isinstance()
和 issubclass()
函数:
isinstance("hello", int | str)
# True
在 Python 的早期版本中,增加了类型别名,以允许我们创建表示用户定义类型的别名。在 Python 3.9 或更早的版本中,可以这样写:
FileName = str
def parse(file: FileName) -> None:
...
这里 FileName
是基本 Python 字符串类型的别名。不过,从 Python 3.10开始,定义类型别名的语法将改为:
FileName: TypeAlias = str
def parse(file: FileName) -> None:
...
这个简单的更改将使程序员和类型检查器更容易区分普通变量赋值和类型别名。此更改也是向后兼容的,因此你不必更新任何使用类型别名的现有代码。
除了这两个变化之外,类型模块还有其他改进 —— 即 PEP 612 中的参数规范变量(Parameter Specification Variables)。但是,这种代码在大多数 Python 代码库中并不常见,因为它们用于将一个可调用(callable)的参数类型转给另一个可调用的参数类型(例如在 decorator 中)。如果你有这样的用例,请查看上面提到的 PEP。
从 Python 3.10 开始,你可以使用 int.bit_count()
来计算整数的二进制表示形式的位计数(1的数量)。这也被称为数量统计(Population Count/popcount):
value = 42
print(bin(value))
# '0b101010'
print(value.bit_count())
# 3
这当然很好,但是呢,其实实现这个函数一点都不难,只需一行代码:
def bit_count(value):
return bin(value).count("1")
尽管如此,这是又一个方便的函数,在某些时候可能会派上用场,而这些有用的小特性正是 Python 如此受欢迎的原因之一,似乎所有东西都是开箱即用的。
Distutils
被弃用(Deprecated)在新版本中,不只是添加了一些新东西,而且还弃用/删除了一些东西。这包括 distutils
包,该包在 3.10 中已被弃用,将在 3.12 中移除。这个包已经被 setuptools
和 packaging
替代一段时间了,所以如果你使用这两个工具中的任何一个,那么应该没有问题。尽管如此,你还是应该检查代码中 distutils
的使用情况,并开始准备在不久的将来摆脱它。
Python 上下文管理器对于打开/关闭文件、处理数据库连接和很多其他事情都非常有用,在 Python 3.10 中,它们的语法将有一点高质量的改进。这个改变允许带圆括号的上下文管理器跨多行,如果你想用一个 with
语句创建多行,这是很方便的:
with (
open("somefile.txt") as some_file,
open("otherfile.txt") as other_file,
):
...
from contextlib import redirect_stdout
with (open("somefile.txt", "w") as some_file,
redirect_stdout(some_file)):
...
从上面可以看到,我们甚至可以在紧接着的另一个上下文管理器中引用由上一个上下文管理器(... as some_file
)创建的的变量!
这些只是 Python 3.10 中可用的很多新格式中的两种。这个改进后的语法非常灵活,所以我不打算展示每一个可能的格式选项,因为我非常确定无论你将在 Python 3.10 中使用什么,它都很可能能正常工作。
正如最近发布的所有 Python 版本一样,Python 3.10 也带来了一些性能提升。首先是对 str()
、 bytes()
和 bytearray()
构造函数的优化,它们的速度应该快 30% 左右(代码段改编自 Python bug tracker 示例):
~ $ ./python3.10 -m pyperf timeit -q --compare-to=python "str()"
Mean +- std dev: [python] 81.9 ns +- 4.5 ns -> [python3.10] 60.0 ns +- 1.9 ns: 1.36x faster (-27%)
~ $ ./python3.10 -m pyperf timeit -q --compare-to=python "bytes()"
Mean +- std dev: [python] 85.1 ns +- 2.2 ns -> [python3.10] 60.2 ns +- 2.3 ns: 1.41x faster (-29%)
~ $ ./python3.10 -m pyperf timeit -q --compare-to=python "bytearray()"
Mean +- std dev: [python] 93.5 ns +- 2.1 ns -> [python3.10] 73.1 ns +- 1.8 ns: 1.28x faster (-22%)
另一个更值得注意的优化(如果你使用类型注释)是函数参数及其注释不再在运行时(runtime)计算,而是在编译时计算。这使得创建一个带有参数注释的函数的速度提高了大约2倍。
除此之外,Python 核心的各个部分还有更多的优化。你可以在 Python bug tracker 的下列问题中找到更多的细节: bpo-41718、 bpo-42927 和 bpo-43452。
你肯定已经听说过的一个重要特性是结构模式匹配(Structural Pattern Matching)。我们都知道其他编程语言如何使用 switch/case 语句,但考虑到这是 Python,嗯,它不仅仅是简单的 switch/case 语法,而且还添加了一些强大的功能,我们应该研究研究。
模式匹配最基本的形式是由 match
关键词和表达式组成,然后根据接着的 case
语句中的模式测试得出结果:
def func(day):
match day:
case "Monday":
return "Here we go again..."
case "Friday":
return "Happy Friday!"
case "Saturday" | "Sunday": # Multiple literals can be combined with `|`
return "Yay, weekend!"
case _:
return "Just another day..."
在这个简单的示例中,我们使用 day
变量作为表达式,然后将其与 case
语句中的单个字符串进行比较。除了字符串字面值的情况外,你还会注意到最后一种情况,它使用 _
通配符,这相当于其他语言中存在的默认关键词。不过,这个通配符可以省略,在这种情况下实际上返回 None
。
在上面的代码中需要注意的另一点是 |
的使用,这使得使用 |
(或)操作符组合多个文本成为可能。
正如我所提到的,这个新的模式匹配并不只有基本的语法,而是带来了一些额外的特性,比如复杂模式的匹配:
def func(person): # person = (name, age, gender)
match person:
case (name, _, "male"):
print(f"{name} is man.")
case (name, _, "female"):
print(f"{name} is woman.")
case (name, age, gender):
print(f"{name} is {age} old.")
func(("John", 25, "male"))
# John is man.
在上面的代码段中,我们使用元组
作为要匹配的表达式。然而这并不局限于元组,任何可迭代对象 iterable 都可以工作。另外,正如上面看到的,_
通配符也可以在复杂模式中使用,而不仅仅是像前面的示例中那样单独使用。
使用纯元组或列表可能并不总是最好的方法,所以如果你更喜欢使用类,那么可以用下面的方法重写:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
gender: str
def func(person): # person is instance of `Person` class
match person:
# This is not a constructor
case Person(name, age, gender) if age < 18: # guard for extra filtering
print(f"{name} is a child.")
case Person(name=name, age=_, gender="male"): # Wildcard ("throwaway" variable) can be used
print(f"{name} is man.")
case Person(name=name, age=_, gender="female"):
print(f"{name} is woman.")
case Person(name, age, gender): # Positional arguments work
print(f"{name} is {age} years old.")
func(Person("Lucy", 30, "female"))
# Lucy is woman.
func(Person("Ben", 15, "male"))
# Ben is a child.
这里我们可以使用类似类构造函数的模式来匹配类的属性。当使用这种方法时,还可以将单个属性捕获到变量中(与前面展示的元组一样),然后我们可以在各自的 case
主体中使用。
上面我们还可以看到模式匹配的一些其他特性,比如在第一个 case
语句中,它是一个向导,这是一个遵循模式的 if 条件。如果按值进行匹配还不够,需要添加一些附加的条件检查,那么这种方法会很有用。再看看其他情况,我们还可以看到关键词(例如 name = name
)和位置参数都可以使用类似构造函数的语法,对于 _
变量也是如此。
模式匹配还允许使用嵌套模式。这些嵌套模式可以使用任何可迭代对象,包括类似构造函数的对象或对象内更多的可迭代对象:
match users:
case [Person(...)]:
print("One user provided...")
case [Person(...), Person(...) as second]: # `as var` can be used to capture subpattern
print(f"There's another user: {second}")
case [Person(...), Person(...), *rest]: # `*var` can be used as unpacking
print(...)
这种复杂模式中,将子模式捕获到变量中以便进行进一步处理可能非常有用。这可以通过使用 as
关键词来完成,如上面第二个 case
所示。
最后,*
操作符可用于“解压缩(unpack)”模式中的变量,_
通配符也可以使用 *_
模式。
如果你想看到更多的例子和完整的教程,请查看 PEP 636。
Python 3.10 带来了很多有趣的新特性,但这是 alpha 版(下个月即将发布 beta 版),还没经过充分的测试。因此,现在就开始在生产中使用它绝对不是一个好主意。所以,最好坐等10月份的正式版发布,有空就刷刷 Python 3.10 新闻页面。
话虽如此,用第一个 beta 版本(将在5月3号发布)进行测试运行,看看你现有的代码库是否与所有即将到来的更改、弃用或删除的函数/模块兼容,这当然是个未雨绸缪的好主意。
原文: https://towardsdatascience.com/all-the-important-features-and-changes-in-python-3-10-e3d1fe542fbf