Python 3.10 - 新版Python中的5大特点

130 阅读10分钟

夏天刚刚开始,这意味着我们又有了新的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测试版,首先,从这里获取安装文件 链接 并运行该安装文件。然后就是通过设置。

Python 3.10 Installation window for Windows

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]

Recommendation Systems

说实话,我是可以接受的。一般来说,我对其他语言中的switch-case语句也不是很喜欢。它通常意味着架构可以做得更好,或者你可以使用多态性和字典等概念。

话虽如此,在某些情况下,switch-case语句还是很有用的。除此之外,模式匹配不仅仅是一个简单的switch-case语句。它的真正力量可以在类型和形状比较中找到。

1.2 Python 3.10 特性

使用这个Python特性的最简单方法是将一个变量与几个值进行比较。有两个新的关键字matchcase。那么上面的例子可以这样写下来。

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')

注意,在第三个选项中,我们只对列表的第三个元素使用了通配符。这就是通配符!cool

我可以想象这有很多可能性,特别是在处理大量数据 和有边缘情况时。另一个有趣的事情是,你可以对集合进行部分匹配。比如说图元:

# 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的错误信息可能具有误导性和迷惑性。这就是为什么这个功能要来帮助我们。这感觉是一个重要的话题,它将使我们的日常工作变得更容易,我真的很期待它。

Programming

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的解析器取代。这开启了许多可能性,我们可以期待许多利用这一变化的功能。不管怎么说,这些功能中最先出现的是括号内的上下文管理器。

Computer

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版本都会有一组类型提示功能。从我们的角度来看,这个是这类特性中最重要的。

Computer

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.用于调试的精确的行号

你是否注意到,有时调试器并不显示代码中出现错误的那一行的真实编号?它在大多数时候确实很好用,但它并不总是最可靠的工具。

Partial

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项目的方式。

谢谢您的阅读!