夏天刚刚开始,这意味着我们又有了新的Python版本的测试版。今年秋天,我们期待着Python 3.10的第十次迭代,计划在2021年10月4日发布。在那之前还有很长的路要走,然而,随着最新的预览版3.10.0b3几周前发布,我们已经可以讨论新特性了。在这篇文章中,我们将探讨一些我们发现的有趣的功能,我们认为这些功能将使我们的代码更加简洁。每一个Python版本都有几个大的主题和几个小的特性,在这篇文章中,我们将介绍五个我们认为会产生最大影响的特性。
0.安装Python 3.10 Betta 3
Linux
要在Linux机器上安装Python 3.10的最新betta版本,首先,用wget获取安装压缩包 。
wget https://www.python.org/ftp/python/3.10.0/Python-3.10.0b3.tgz
将其解压
tar xzvf Python-3.10.0b3.tgz
cd Python-3.10.0b3
./configure --prefix=$HOME/python-3.10.0b3
然后运行make文件
make
make install
$HOME/python-3.10.0b3/bin/python3.10
Windows和Mac
要在Windows或Mac机器上安装最新的Python 3.10测试版,首先,从这里获取安装文件 链接 并运行该安装文件。然后就是通过设置。

1.结构模式匹配
这个功能肯定会产生最大的影响。如果你对其他语言中的switch-case语句很熟悉,那么这个语句就是那个,但是是类固醇。等等,在Python中到现在为止还没有switch-case机制吗?为什么它被称为模式匹配?有什么区别呢?让我们来看看。
1.1 当前的问题
是的,Python到现在为止还不支持 switch-case 语句,或者类似的概念。很奇怪,我知道。曾经有几次尝试 在语言中加入这个概念,然而,它们总是被拒绝。没有人能够提出一个能够与Python的 语法和语言的整体精神适当结合的实现。这意味着,如果你想检查多个值,你需要做这样的事情。
def http_error(status):
if 400:
return "Bad request"
elif 404:
return "Not found"
elif 418:
return "Rock is not dead"
或者类似这样的事情:
def http_error(status):
return_value = {
400: "Bad request",
404: "Not found",
418: "Rock is not dead" }
return return_value[status]
说实话,我是可以接受的。一般来说,我对其他语言中的switch-case语句也不是很喜欢。它通常意味着架构可以做得更好,或者你可以使用多态性和字典等概念。
话虽如此,在某些情况下,switch-case语句还是很有用的。除此之外,模式匹配不仅仅是一个简单的switch-case语句。它的真正力量可以在类型和形状比较中找到。
1.2 Python 3.10 特性
使用这个Python特性的最简单方法是将一个变量与几个值进行比较。有两个新的关键字match和case。那么上面的例子可以这样写下来。
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "Rock is not dead"
case _:
return "Well, this is embaresing"
所以,你知道的,一个常规的switch-case语句。最后一个 case _ 是默认值--通配符,所以如果状态的值不是400、404或418,它将返回 "嗯,这很尴尬"作为结果。这是可选的,所以如果你不包括它,而出现了一些范围外的值,那么这个行为就是一个无用的行为。这个通配符非常强大,可以帮助我们进行更复杂的模式匹配,我们稍后会看到。
如果你想在某种情况下做同样的事情,你也可以使用连锁值。比如说:
def ide_full_name(short_name):
match short_name:
case "PyC":
return "PyCharm"
case "VSC":
return "Visual Studio Code"
case "VS2017" | "VS2019" | "VS2022":
return "Visual Studio"
case _:
return "Not Supported"
好了,这些是简单的例子,现在让我们看看为什么这个Python 3.10的特性真的很酷。
1.2.1 结构模式匹配和集合
正如你所想象的,你可以使用匹配-case组合来比较完整的集合。例如,如果你想检查列表中是否有某个值,你可以这样做:
match x:
case [1, 2] :
print('First option')
case [1, 2, 3] :
print('Second option')
真正有趣的可能性来自通配符。你可以做这样的事情:
match x:
case [1, 2] :
print('First option')
case [1, 2, 3] :
print('Second option')
case [1, 2, _]:
print('Third option')
case _:
print('Default option')
注意,在第三个选项中,我们只对列表的第三个元素使用了通配符。这就是通配符!![]()
我可以想象这有很多可能性,特别是在处理大量数据 和有边缘情况时。另一个有趣的事情是,你可以对集合进行部分匹配。比如说图元:
# point is an (x, y) tuple
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
1.2.2 结构模式匹配和类
对于类,你可以做更多有趣的事情。例如,像这样的事情:
class Rectangle:
hight: int
width: int
def check_rectangle(rec):
match rec:
case Rectangle(hight=0, width=0):
print("The rectangle is a point")
case Rectangle(hight=0, width = width):
print("The rectangle is a vertical line.")
case Rectangle(hight=hight, width = 0):
print("The rectangle is a horizontal line.")
case Rectangle():
print("Just a normal rectangle, folks.")
case _:
print("Not a rectangle")
这里我们有一个矩形类和一个check_rectangle()函数。在这个函数中,你可以找到模式匹配。这里你可以看到,你实际上可以将一个对象与某个模式进行比较。这些模式类似于构造函数。这可以通过所谓的guards进一步扩展。从本质上讲,守卫只是模式中的一个if条款。因此,如果我们扩展前面的例子:
class Rectangle:
hight: int
width: int
def check_rectangle(rec):
match rec:
case Rectangle(hight=0, width=0):
print("The rectangle is a point")
case Rectangle(hight=0, width = width):
print("The rectangle is a vertical line.")
case Rectangle(hight=hight, width = 0):
print("The rectangle is a horizontal line.")
case Rectangle(hight=hight, width=width) if hight == width :
print("The rectangle is a square")
case Rectangle():
print("Just a normal rectangle, folks.")
case _:
print("Not a rectangle")
我们可以这样使用它:
rec = Rectangle()
rec.hight = 0
rec.width = 0
check_rectangle(rec)
rec = Rectangle()
rec.hight = 11
rec.width = 0
check_rectangle(rec)
rec = Rectangle()
rec.hight = 0
rec.width = 11
check_rectangle(rec)
rec = Rectangle()
rec.hight = 33
rec.width = 33
check_rectangle(rec)
The rectangle is a point
The rectangle is a horizontal line.
The rectangle is a vertical line.
The rectangle is a square
2.更好的错误信息
一般来说,Python的错误信息可能具有误导性和迷惑性。这就是为什么这个功能要来帮助我们。这感觉是一个重要的话题,它将使我们的日常工作变得更容易,我真的很期待它。
2.1 当前问题
有几种类型的错误,目前的Python化身并没有提供足够的信息。例如,观察以下代码的SytaxError:
var newArticle = new Article
{
Title = "C# 10 - Top 5 new features",
Category = ".NET",
ReleaseDate = DateTime.Now()
}
File "<ipython-input-5-e9aa07ca09db>", line 2
processed_data = process_data(data)
^
正如你所看到的,方括号在第一行的末尾丢失了。然而,SyntaxError报告了第二行的问题。如果我们忘记关闭字符串引号,也会发生类似的情况。
word = "BirdIsThe
processed_data = process_data(data)
File "<ipython-input-6-44253c1ee7d8>", line 1
word = "BirdIsThe
^
SyntaxError: EOL while scanning string literal
SyntaxError: invalid syntax
在这种情况下,会有更大的误导性,因为会报告EOL或EOF。最后,我们可能面临的最后一个问题是如果我们试图做这样的事情:
some_function(x, y for y in range(11))
File "<ipython-input-8-8b1dcc7e2f02>", line 1
some_function(x, y for y in range(11))
^
SyntaxError: Generator expression must be parenthesized
现在,这个例子可能有点简单,因为我们的函数中只有两个参数。然而,SyntaxError 只是检测问题的地方。
2.2 Python 3.10 特性
有几个地方我们将从Python 3.10获得更好的信息。让我们从上面一章中显示的问题开始。如果你像上面的例子那样忘记关闭括号,你会得到这样的结果。
data = [2, 9, 11, 33, 56, 93, 111,
^
SyntaxError: '[' was never closed
或者如果你忘记关闭字符串:
word = "BirdIsThe
^
SyntaxError: unterminated string literal (detected at line 1)
或者如果你弄乱了你的生成器表达式:
some_function(x, y for y in range(11))
^^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized
所有这些信息都是比较具体的,具体的。然而,这还不是全部。Python 3.10也改进了其他信息。如果你忘记在语句后面加两个点,现在你会更明确地知道。
if (2 > 9)
if (2 > 9)
^
SyntaxError: expected ':'
或者如果你在提到的语句中使用了 = 而不是 ==。
if (2 = 9):
pass
if (2 = 9)
^
SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='?
另外,如果你在集合中忘记了逗号,Python 3.10也会帮助你:
list = [2, 9, 11, 33 56, 93]
File "<stdin>", line 1
list = [2, 9, 11, 33 56, 93]
谈到集合,如果你忘记给一个字典的键赋值,Python 3.10 会让你知道:
dict = { 'a' : 2, 'b' : 9, 'c' : , 'd' : 33 }
File "<stdin>", line 1
dict = { 'a' : 2, 'b' : 9, 'c' : , 'd' : 33 }
^
SyntaxError: expression expected after dictionary key and ':'
还有其他改进的信息,然而我发现这些是最有用的,特别是如果你是一个 乞丐.
3.括号内的上下文管理器
Python 的上一个版本 -Python 3.9,带来了一些有趣的变化。即CPython的基于LL(1)的解析器被一个新的基于PEG的解析器取代。这开启了许多可能性,我们可以期待许多利用这一变化的功能。不管怎么说,这些功能中最先出现的是括号内的上下文管理器。
3.1 目前的问题
如果你试图在当前版本的Python中在多行中使用多个with语句,你就不能用圆括号把它们分开。例如,如果你尝试像这样的东西。
with (open("a_really_long_foo") as foo,
open("a_really_long_bar") as bar):
pass
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "demo.py", line 19
with (open("a_really_long_foo") as foo,
^
SyntaxError: invalid syntax
3.2 Python 3.10 特性
Python 3.10 现在能够处理这个问题。你可以使用下面的任何一个选项来处理这个问题。
# Variation 0
with (CtxManager() as example):
pass
# Variation 1
with (
CtxManager1(),
CtxManager2()
):
pass
# Variation 2
with (CtxManager1() as example,
CtxManager2()):
pass
# Variation 3
with (CtxManager1(),
CtxManager2() as example):
pass
# Variation 4
with (
CtxManager1() as example1,
CtxManager2() as example2
):
pass
# Variation 5
with (
CtxManager1() as example1,
CtxManager2() as example2,
CtxManager3() as example3,
):
pass
4.新的类型联合操作符
这是一个可以简化代码的新特性。每个Python版本都会有一组类型提示功能。从我们的角度来看,这个是这类特性中最重要的。
4.1 目前的问题
在当前的Python版本中,如果你想对有一个可以接收不同类型值的参数的函数使用类型提示,你必须使用联合类型。类似这样的情况。
def some_funcion(flexible_parameter: Union[int, string]) -> Union[int, string]:
return flexible_parameter
4.2 Python 3.10 特性
Python 3.10引入了新的联合操作数 - |。这个操作数说的是,某些参数可以是类型1,也可以是类型2。现在,前面的函数可以这样写。
def some_funcion(flexible_parameter: int | string) -> int | string:
return flexible_parameter
5.用于调试的精确的行号
你是否注意到,有时调试器并不显示代码中出现错误的那一行的真实编号?它在大多数时候确实很好用,但它并不总是最可靠的工具。
5.1 目前的问题
如果你使用 sys.settrace 和相关的工具,你可能会注意到,追踪并不是100%的工作。有时行的不正确。这里的可靠性并不像你对这样一种成熟的编程语言所期望的那样。这个问题的本质是,作为事件对象一部分的帧对象的f_lineno属性应该总是包含预期的行数
5.2 Python 3.10 特性
新版本的Python为调试、剖析和覆盖工具带来了更精确和可靠的行号。Python 3.10保证跟踪事件,以及相关的和正确的行号,为所有执行的代码行生成,并且只为执行的代码行生成。
为了做到这一点,Python 3.10不依赖于事件的当前形式的co_lnotab属性。这个版本的Python使用了新的methoco_lines(),它返回一个字节码偏移量和源代码行的迭代器。这种改变是以一种方式进行的,即框架对象的 f_lineno属性将总是包含预期的行数。
总结
在这篇文章中,我们有机会熟悉了新版 Python 将带来的 5 个新特性。说实话,我已经迫不及待地想把它们全部试一遍了。其中一些看起来是重大改进,而且似乎会影响我们组织和编写Python项目的方式。
谢谢您的阅读!




