Python 3.10中的新内容

171 阅读4分钟

Python 3.10刚刚发布,这意味着我们终于可以看到有哪些新特性了。所以我们来看看,好吗?

结构模式匹配

这是一个大问题--这可能是自2008年Python 3.0发布以来最大的语法补充。

结构模式匹配 "的真正含义是,现在我们有了一种方法,可以根据一段数据的结构,而不是逻辑条件来编写条件语句。它是通过一个新的match 关键字和多个case 子句完成的:

status = get_http_status()
match status:
    case 200:
        print('OK')
    case 404:
        print('Not Found')
    case _:
        print(f'Unknown status: {status}')

你可能会认为这看起来与其他语言中另一个流行的功能非常相似:开关语句。你是对的--它绝对可以作为switch-case的替代品,但match 做的更多。这里有几个例子。

匹配一个数据结构中的元素

command = ['move', 'right']
match command:
    case ['move', direction]:
        print(f'Player moved in {direction} direction.')
    case ['attack', enemy]:
        print(f'Player attacked {enemy}.')
    case _:
        print('Unknown command.')

# Output:
# Player moved in right direction.

匹配一个对象中的属性

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

vec = Vector(3, 5)

match vec:
    case Vector(x, 0):
        print(f'Vector lies on Y-axis with {x=}')
    case Vector(0, y):
        print(f'Vector lies on X-axis with {y=}')
    case Vector(x, y):
        print(f'Vector lies on {x=}, {y=}')

# Output:
# Vector lies on x=3, y=5

使用传播语法匹配可变数量的属性

msg = {'type': 'collect', 'orbs': 10, 'badges': 30}

match msg:
    case {'type': 'collect', **bag}:
        print('Collected items:')
        for name, count in bag.items():
            print(f'{name} - {count}')
    case {'type': 'drop', **bag}:
        print(f"Dropped items: {', '.join(bag)}")
    case _:
        print('Unknown message')

# Output:
# Collected items:
# orbs - 10
# badges - 30

更好的错误信息

尽管Python在可读性方面很友好,但它的错误信息有时却相当难以理解。幸运的是,Python 3.10 在其错误信息的可读性方面带来了巨大的改进。

其中一些改进的信息是

>>> if rocket.position = event_horizon:
  File "<stdin>", line 1
    if rocket.position = event_horizon:
                       ^
SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='?
>>> collections.namedtoplo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'collections' has no attribute 'namedtoplo'. Did you mean: namedtuple?
>>> values = {x:1, y:2, z w:3}
  File "<stdin>", line 1
    values = {x:1, y:2, z w:3}
                        ^
SyntaxError: ':' expected after dictionary key
>>> foo(x, z for z in range(10), t, w)
  File "<stdin>", line 1
    foo(x, z for z in range(10), t, w)
           ^^^^^^^^^^^^^^^^^^^^
SyntaxError: Generator expression must be parenthesized

新的类型联合运算符

typing 模块的一个非常常见的用法是创建联合数据类型,即可以访问多种数据类型的变量。要做到这一点,我们需要导入typing.Union ,像这样:

def double(value: Union[int, str]) -> Union[int, str]:
    return value * 2

由于这是一个非常普遍的使用情况,我们为相同的功能添加了一个速记:你现在可以使用| 操作符来创建数据类型的联盟:

def double(value: int | str) -> int | str:
    return value * 2

scrict zip() 函数中的参数

Python 内置的zip 函数相当方便,它接收一堆迭代变量,并从每一个迭代变量中返回一个值,直到任何一个迭代变量的值用完。比如说:

nums = [1, 2, 3]
letters = ['a', 'b', 'c']
for letter, num in zip(letters, nums):
    print(f'{letter}: {num}')

# Output:
# a: 1
# b: 2
# c: 3

zip 的问题是,如果其中一个迭代表比另一个迭代表长,那么这些结束值将永远不会出现在压缩包中,而你将无从得知:

nums = [1, 2, 3, 4, 5, 6]
letters = ['x', 'y', 'z']
for letter, num in zip(letters, nums):
    print(f'{letter}: {num}')

# Output:
# x: 1
# y: 2
# z: 3

我们错过了nums 中的4、5和6,而没有办法知道这一点。为了弥补这一点,我们引入了新的strict 参数,如果任何一个迭代项的长度不同,就会抛出一个错误:

nums = [1, 2, 3, 4, 5, 6]
letters = ['x', 'y', 'z']
for letter, num in zip(letters, nums, strict=True):
    print(f'{letter}: {num}')

# Output:
# x: 1
# y: 2
# z: 3
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# ValueError: zip() argument 2 is longer than argument 1

废弃了distutils 模块

distutils 包是创建可分发的 Python 包的老方法,例如上传到PyPI。该包的功能已经被第三方软件包setuptoolspackaging 所取代。正因为如此,它在3.10中被废弃,并将在3.12中被删除。

其他改进

  • 增加了int.bit_count() 方法,用于返回一个数字的二进制表示中的1 位数。
  • 增加了其他typing 模块:TypeAlias 类型,以及用户定义的类型守护与TypeGuard
  • 增加了sys.orig_argv 属性,它包含传递给python 命令的原始args。

总结

今年我们得到了一些非常激动人心的大的更新!要想更详细地了解这个版本中的变化,你可以查看文档

另外,如果你想了解明年Python中会出现什么,你可以关注Python邮件列表中的发展,以及最新的开放PEPs。明年的计划似乎已经非常有趣了。👀