1. FlyPython首页
  2. Python高级话题

Python 3.10 有什么好处?

Python 3.10 中有哪些变化,哪些变化对您很重要?

这周我一直在玩 Python 3.10。我主要致力于解决包含 Python 3.10 新功能的 Python Morsels 练习。我想分享我的发现。

通过改进的错误消息更轻松地进行故障排除

迄今为止,Python 3.10 最大的改进都是相关的改进错误消息。我总是打错字。帮助我快速找出问题所在的错误消息非常重要。

我已经习惯了破译许多 Python 更神秘的错误消息的过程。因此,虽然改进的错误信息非常适合我,这种变化尤其是对于新的Python学习者大。

当我教授 Python 入门课程时,我帮助人们调试的一些最常见的错误是:

  1. 代码块末尾缺少冒号
  2. 代码块中缺少缩进或缩进不正确
  3. 拼写错误的变量名
  4. 从未关闭的括号和大括号

Python 3.10 使 Python 学习者更清楚所有这些错误(以及更多)。

新的 Python 用户经常忘记在:他们的代码块中放置一个在 Python 3.9 中,用户会看到这个神秘的错误消息:

1 
2 
3 
4 
5
$ python3.10 temp.py 70
  File "/home/trey/temp.py", line 4
    if temperature < 65
                       ^
SyntaxError: invalid syntax

Python 3.10 使这一点更加清晰:

1 
2 
3 
4 
5
$ python3.10 temp.py 70
  File "/home/trey/temp.py", line 4
    if temperature < 65
                       ^
SyntaxError: expected ':'

缩进错误也更清晰(这after 'if' statement on line 4是新的):

1 
2 
3 
4 
5
$ python3.10 temp.py 70
  File "/home/trey/temp.py", line 5
    print("Too cold")
    ^
IndentationError: expected an indented block after 'if' statement on line 4

不正确的变量和属性名称现在显示一个建议:

1 
2 
3 
4 
5
$ python3.10 temp.py 70
Traceback (most recent call last):
  File "/home/trey/temp.py", line 4, in <module>
    if temparature < 65:
NameError: name 'temparature' is not defined. Did you mean: 'temperature'?

我真的很兴奋,因为我几乎每天都会在变量名称中输入拼写错误。

没有结束的括号,括号和括号中显示的错误信息也很多更有帮助。

Python 过去常常向我们展示未闭合大括号后下一行代码:

1 
2 
3 
4 
5
$ python3.9 temp.py 70
  File "/home/trey/temp.py", line 6
    elif temperature > 80:
    ^
SyntaxError: invalid syntax

现在它指向未关闭的左大括号:

1 
2 
3 
4 
5
$ python3.10 temp.py 70
  File "/home/trey/temp.py", line 5
    print("Too cold"
         ^
SyntaxError: '(' was never closed

您可以在“Python 3.10 的新增功能”文档更好的错误消息部分中找到有关这些改进的错误消息的更多详细信息

虽然 Python 3.10 确实包含其他更改(如果您感兴趣,请继续阅读),这些改进的错误消息是所有 Python 用户都会注意到的3.10 改进

IDLE 在视觉上更加一致

这是影响 Python 新用户的另一个特性:IDLE 的外观有所改进IDLE 现在使用空格而不是制表符来缩进(与内置 REPL 不同)...,REPL 连续行前面熟悉的内容现在出现在 IDLE 的侧边栏中。

在 IDLE 之前是这样的:

Python 3.10 有什么好处?

现在空闲看起来像这样:

Python 3.10 有什么好处?

看起来更像命令提示符上的 Python REPL,对吧?

zip 功能的长度检查

有一个 Python Morsels 练习叫做strict_zip. 它现在变成了“重新实现这个已经内置的功能”的练习。为了学习如何zip实现仍然有用,但不再有用的日常代码。

为什么没有用?因为zip现在接受一个strict论点!因此,如果您正在使用可能具有不同长度但不应如此的迭代对象,strict=True现在建议在使用 zip 时传递

结构模式匹配

每个人都在谈论的 Python 3.10 大特性是结构模式匹配。此功能非常强大,但可能与大多数 Python 用户无关。

关于此功能的一个重要说明:matchcase仍然是允许的变量名称,因此您现有的所有代码都应该继续工作(它们是软关键字)。

匹配可迭代对象的形状和内容

您可以将 new match/case语句视为元组解包,而不仅仅是长度检查

比较来自 Django 模板标签的这段代码

1 
2 
3 
4
args = token.split_contents()
if len(args) != 5 or args[1] != 'for' or args[3] != 'as':
    raise TemplateSyntaxError("'%s' requires 'for string as variable' (got %r)" % (args[0], args[1:]))
return GetLanguageInfoNode(parser.compile_filter(args[2]), args[4])

对于重构为使用结构模式匹配的同一个片段:

1 
2 
3 
4 
5
match token.split_contents():
    case [name, "for", code, "as", info]:
        return GetLanguageInfoNode(parser.compile_filter(code), info)
    case [name, *rest]:
        raise TemplateSyntaxError(f"'{name}' requires 'for string as variable' (got {rest!r})")

请注意,第二种方法允许我们描述我们将数据解包到的变量数量和要解包的名称(就像元组解包一样),同时还可以将第二个和第三个值与字符串for和 进行匹配as如果这些字符串没有出现在预期的位置,我们会引发一个适当的异常。

结构模式匹配对于实现简单的解析器非常方便,比如 Django 的模板语言。我期待在 2025 年(Python 3.9 支持结束后)看到 Django 重构的模板代码。

复杂类型检查

结构模式匹配也擅长类型检查。Python 中通常不鼓励强类型检查,但它确实不时出现。

最常见的地方,我看到isinstance检查是在运算符重载dunder方法(__eq____lt____add____sub__,等)。我已经升级一些Python俎解决方案进行比较和对比matchcaseisinstance而我更详细的发现它在某些情况下,还偶尔有点更清晰。

例如这个代码片段(同样来自 Django):

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15
if isinstance(value, str):  # Handle strings first for performance reasons.
    return value
elif isinstance(value, bool):  # Make sure booleans don't get treated as numbers
    return str(value)
elif isinstance(value, (decimal.Decimal, float, int)):
    if use_l10n is False:
        return str(value)
    return number_format(value, use_l10n=use_l10n)
elif isinstance(value, datetime.datetime):
    return date_format(value, 'DATETIME_FORMAT', use_l10n=use_l10n)
elif isinstance(value, datetime.date):
    return date_format(value, use_l10n=use_l10n)
elif isinstance(value, datetime.time):
    return time_format(value, 'TIME_FORMAT', use_l10n=use_l10n)
return value

可以用这个代码片段代替:

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17
match value:
    case str():  # Handle strings first for performance reasons.
        return value
    case bool():  # Make sure booleans don't get treated as numbers
        return str(value)
    case decimal.Decimal() | float() | int():
        if use_l10n is False:
            return str(value)
        return number_format(value, use_l10n=use_l10n)
    case datetime.datetime():
        return date_format(value, 'DATETIME_FORMAT', use_l10n=use_l10n)
    case datetime.date():
        return date_format(value, use_l10n=use_l10n)
    case datetime.time():
        return time_format(value, 'TIME_FORMAT', use_l10n=use_l10n)
    case _:
        return value

请注意每个条件有多短。这种case语法肯定需要一些时间来适应,但我确实发现在isinstance像这样的链中阅读更容易一些

用钥匙一分为二

Python 的bisect模块对于在排序列表中快速查找项目非常方便。

对我来说,该bisect模块主要是提醒我很少需要关心我在计算机科学课程中学到的二分搜索算法。但是对于那些确实需要在排序列表中查找项目的时候,这bisect很棒。

从 Python 3.10 开始,bisect模块中的所有二进制搜索助手现在都接受一个key参数。因此,您现在可以在不区分大小写的字符串列表中快速搜索您要查找的字符串。

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12
>>> fruits = sorted(['Watermelon','loquat', 'Apple', 'jujube'], key=str.lower)
>>> fruits
['Apple', 'jujube', 'loquat', 'Watermelon']
>>> import bisect
>>> bisect.insort(fruits, 'Lemon', key=str.lower)
>>> fruits
['Apple', 'jujube', 'Lemon', 'loquat', 'Watermelon']
>>> i = bisect.bisect(fruits, 'lime', key=str.lower)
>>> fruits[i] == 'lime'
False
>>> fruits[i]
'loquat'

在 Python 3.10 之前,进行涉及key函数的搜索非常棘手

数据类的插槽

有一个数据类(尤其是一个冻结的)并想让它更节省内存?您可以添加__slots__属性,但您需要自己键入所有字段名称。

1 
2 
3 
4 
5 
6 
7
from dataclasses import dataclass

@dataclass
class Point:
    __slots__ = ('x', 'y')
    x: float
    y: float

在 Python 3.10 中,您现在可以slots=True改用:

1 
2 
3 
4 
5 
6
from dataclasses import dataclass

@dataclass(slots=True)
class Point:
    x: float
    y: float

这个特性实际上包含在原始数据类实现中,但在 Python 3.7 发布之前被删除(如果用户表示感兴趣,Guido 建议将它包含在更高的 Python 版本中,我们做到了)。

创建__slots__手动添加的数据类将不允许使用默认字段值,这就是为什么slots=True如此方便。但是有一个非常小的怪癖slots=True使用super时调用中断,slots=True因为这会导致创建一个新的类对象,这打破了super魔力但是除非您在冻结数据类super().__setattr____post_init__方法中使用调用而不是调用object.__setattr__,否则这种怪癖可能不会影响您。

类型注释改进

如果您使用类型注释,现在使用运算符(除了更容易进行类型联合类型注释领域的其他重要补充包括参数规范变量类型别名用户定义的类型保护我仍然不经常使用类型注释,但是对于使用类型注释的 Python 开发人员来说,这些功能非常重要。|typing.Union

此外,如果您正在内省注释,inspect.get_annotations建议调用该函数而不是__annotations__直接访问或调用该typing.get_type_hints函数。

检查默认文件编码问题

现在,您还可以在未指定显式文件编码时要求 Python 发出警告(这在编写跨操作系统兼容代码时非常重要)。

只需运行 Python,-X warn_default_encoding如果您没有为打开文件的每个人指定编码,您将看到一条响亮的错误消息:

1 
2 
3 
4
$ python3.10 -X warn_default_encoding count_lines.py declaration-of-independence.txt
/home/trey/count_lines.py:3: EncodingWarning: 'encoding' argument not specified
  with open(sys.argv[1]) as f:
67

还有更多

上述更改是我在上周更新 Python Morsels 练习时发现有用的主要更改。不过,Python 3.10 中还有更多变化。

以下是我研究的更多内容,并计划稍后使用:

  • 仅关键字数据类字段
  • fileinput.input(方便的用于处理标准输入一个文件)的功能现在可以接受encoding参数
  • importlib 弃用:我的一些动态模块导入代码使用的功能现在在 Python 3.10 中已弃用(如果您的代码也需要更新,您会注意到明显的弃用警告)
  • 字典视图有一个mapping属性现在:如果你让你自己的字典对象,你应该添加一个mapping属性,你keys/ values/items意见,以及(这肯定会在Python俎作物保健操在未来)
  • 在单个with块中使用多个上下文管理器时,现在可以使用括号将它们包装到下一行(这实际上是在 Python 3.9 中添加的,但不是官方的
  • 的名称标准库模块和内置模块现在包括sys.stdlib_module_namessys.builtin_module_names:我偶尔会需要第三方和动态标准库模块,这使得一个更容易区分
  • sys.orig_argv包括命令行参数完整列表(包括 Python 解释器和传递给它的所有参数),这在检查 Python 进程的启动方式或使用相同参数重新启动 Python 进程时非常有用

概括

结构模式匹配很棒,其他各种语法、标准库和内置改进也很可爱。但迄今为止最大的改进是新的错误消息。

您知道有什么比 Python 3.10 中的新错误更好的消息吗?
Python 3.11 将包含更好的错误消息

原创文章,作者:flypython,如若转载,请注明出处:http://flypython.com/advanced-python/532.html