Python 3.14 的 3 个语法更新将使您的代码更安全、更好用

PEP 745 所述,Python 3.14 计划于 2025 年 10 月 7 日发布最终版本,其中包含令人兴奋的更新和增强功能。

它通过引入实验性的新解释器、优化各种内置模块和改进文件 I/O 操作,大大提高了性能。

因此,只需升级到 3.14 版,Python 程序就能运行得更快,而无需对现有代码进行任何修改。

由于大多数 Python 开发人员主要专注于构建应用程序,因此没有必要了解这些性能增强的每一个底层细节。

不过,Python 3.14 引入了三个语法变化,它们将影响我们编写 Python 程序的方式。

在本文中,我将探讨并清楚地解释这些变化,以帮助您在不断发展的 Python 生态系统中保持与时俱进。

1. 禁止在 finally 块中使用控制流语句

初级 Python 开发人员可能会犯一个常见错误–在 finally 块中编写控制流语句,包括 returnbreak continue

为什么会有问题呢?

元素周期表

因为 finally 块中的控制流语句总是会执行,所以它们会覆盖相应 try/except 块中的任何控制流,并可能导致意想不到的行为。

例如,下面的程序会打印出什么?

def return_example():
    try:
        print("In try block")
        return "Try block return"
    except Exception as e:
        print(f"Exception: {e}")
    finally:
        print("In finally block")
        return "Finally block return"

result = return_example()
print(f"Result: {result}") 

是的,try 代码块内的 return 语句是无用的,result变量将被 finally 代码块的返回值覆盖:

In try block
In finally block
Result: Finally block return

如果在 finally 块内有 continue break 语句,也会出现类似问题:

def break_example():
    for i in range(5):
        try:
            print(f"In try block, i = {i}")
            if i == 3:
                break  # Should exit loop at i=2
        except Exception as e:
            print(f"Exception: {e}")
        finally:
            print("In finally block")
            break  

break_example()
# In try block, i = 0
# In finally block

正如上面的代码所示,由于 finally 块总是执行,当 i == 0 时,函数立即结束了循环。

危险在于,即使你编写了与示例类似的程序,Python 也不会发出任何警告。这可能是出现意外错误的根源。

幸运的是,Python 3.14 将为此做出重大改变。

正如 PEP 765 所规定的,当 returnbreak continue 语句出现在 finally 代码块中时,Python 3.14 将发出SyntaxWarning

这看起来很微妙,但可以节省我们大量的调试时间。

finally 块仅用于清理操作;不要在其中放置控制流语句。

2. 无括号异常处理

正确的异常处理对于代码的健壮性至关重要。

目前,如果我们需要捕获多个异常,就需要使用括号:

def divide(a, b):
    try:
        result = a / b
        return result
    except (ZeroDivisionError, TypeError):
        print(f"Wrong input: {a} and {b}")
        raise

为了更简洁,Python 3.14 将取消捕获多个异常时对括号的要求。因此,我们可以将上面的示例重写如下:

def divide(a, b):
    try:
        result = a / b
        return result
    except ZeroDivisionError, TypeError:
        print(f"Wrong input: {a} and {b}")
        raise

不过,这一点比较棘手,因为在使用 as 子句捕获异常实例时,仍然必须使用括号:

def divide(a, b):
    try:
        result = a / b
        return result
    except (ZeroDivisionError, TypeError) as e:
        print(f"Error: {e}")
        raise e

为什么需要?

正如 PEP 758 中解释的那样,不需要括号可能会造成混乱,因为不清楚到底是什么被分配给了目标。

就我个人而言,我对这一更新的印象并不深刻,但知道这一点还是很好的。如果有一天您在代码审查时看到了这段不需要括号的代码,请不要这么快就与您的同事争论。先看看是不是 Python 3.14。😄

3. 类型注解的延迟评估

Python 的类型提示有一个长期存在的烦恼。

例如,下面的代码会引发 NameError

class Node:
    def __init__(self, value: int, next: Node):  
        self.value = value
        self.next = next
# NameError: name 'Node' is not defined. 

原因是 Python 在读取这段代码时,还没有完成对 Node 类的定义,就试图在类型提示中使用它。

简单地说,如果我们有一个类在注解中引用了它自己或另一个尚未定义的类,就会引发 NameError

有两种方法可以解决这个问题,但都不够优雅。

方法 1:使用字符串文字(引用未定义的注解):

class Node:
    def __init__(self, value: int, next: 'Node'):  
        self.value = value
        self.next = next

一种方法是引用未定义类型 Node。当一个大型程序既有引号类型提示,又有未引号(正常)类型提示时,这可能会显得很混乱。但无论如何,它可以解决这个问题。

方法 2:使用 from __future__ import annotations

Python 社区曾经尝试解决这个问题。从 Python 3.7 开始,我们可以导入一个 “神奇 “的模块 annotations 来使 NameError 消失:

from __future__ import annotations

class Node:
    def __init__(self, value: int, next: Node):  
        self.value = value
        self.next = next

这种特殊的导入方式使 Python 在运行时将所有注释都视为字符串字面量,这就是它能够工作的原因。这实际上是当前 Python 编程的首选方式。

然而,我们为什么要导入它呢?如果开发人员不知道这一点,就会对这个 “神奇 “模块的用法感到困惑。

幸运的是,Python 3.14 将使我们的生活变得更轻松。

在 Python 3.14 中,函数、类和模块注解不再在定义时执行。

取而代之的是,它们被存储在一种特殊的延迟形式中,只有在需要时才被求值。这意味着编写 def f(x: MyType) -> OtherType: ...时不再在导入时对 MyType OtherType 进行评估,而是将评估推迟到实际执行运行时反省时进行。(正如 PEP 649PEP 749 所规定的那样。)

这一修改提高了 Python 代码的可读性和优雅性。

这意味着不需要用引号包装正向引用,也不需要使用 from __future__ import 注释作为变通方法。我们可以放心地在注解中使用未定义的类名,因为 Python 3.14 会处理它。

结论

在即将到来的 Python 3.14 中,在 finally 代码块中隐藏错误将受到警告,在异常处理中可以避免使用某些括号,类型提示更便宜、更易用。

这些并不是革命性的语法变化,但它引入的更新是经过深思熟虑的改进,使 Python 代码更安全、更简洁、更直观。

你也许感兴趣的:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注