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 块中编写控制流语句,包括 return、break 或 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 所规定的,当 return、break 或 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 649 和 PEP 749 所规定的那样。)

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

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

结论

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

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

发表回复

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