Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 640 – 未使用变量语法

作者:
Thomas Wouters <thomas at python.org>
状态:
拒绝
类型:
标准跟踪
创建:
2020-10-04
Python 版本:
3.10
历史记录:
2020-10-19
决议:
Python-Dev 消息

目录

拒绝说明

被指导委员会拒绝: https://mail.python.org/archives/list/python-dev@python.org/message/SQC2FTLFV5A7DV7RCEAR2I2IKJKGK7W3/

摘要

本 PEP 提出了一种用于 _未使用变量_ 的新语法,提供了一个可以赋值但不能使用的伪名称。赋值实际上不会发生,而是会丢弃该值。

动机

在 Python 中,人们经常需要进行赋值,而实际上不需要结果。按照惯例,人们使用 "_" 或者类似 "unused" 的名称(或者以 "unused" 作为前缀)来表示这种情况。这在 _解包赋值_ 中最为常见

x, unused, z = range(3)
x, *unused, z = range(10)

它也用在 for 循环和推导中

for unused in range(10): ...
[ SpamObject() for unused in range(10) ]

在这些情况下使用 "_" 可能最常见,但它可能会与在国际化中使用 "_" 发生冲突,在国际化中,类似 gettext.gettext() 的调用被绑定到 "_",并用于标记要翻译的字符串。

在将模式匹配添加到 Python 的提案中(最初为 PEP 622,现在拆分为 PEP 634PEP 635PEP 636),"_" 具有 _附加的_ 特殊含义。它是一个通配符模式,用于在可以分配变量的位置,表示应该匹配任何东西,但不能分配给任何东西。选择 "_" 与在其他语言中使用 "_" 相匹配,但与 Python 中其他地方使用 "_" 的语义差异很大。

本 PEP 建议允许使用一个特殊标记 "?" 来代替赋值中的任何有效名称。它具有 "_" 的大多数优点,而不影响该常规变量的其他用途。允许使用相同的通配符模式将使模式匹配和解包赋值更加一致。

理由

将某些变量标记为未使用是一个有用的工具,因为它有助于提高代码的目的性。它使代码的读者和自动代码整理器清楚地知道某个特定变量是 _故意_ 未使用的。

然而,尽管有这种惯例,"_" 并不是一个特殊的变量。该值仍然会被分配,它引用的对象仍然会保留到范围结束,并且它仍然可以使用。使用 "_" 来表示未使用变量也并非完全无处不在,因为它与传统的国际化冲突,它并不明显这是一个常规变量,并且它不像名为 "unused" 的变量那样明显地未使用。

在模式匹配提案中,由于它在单独的范围内,使用 "_" 作为通配符模式避免了使用 "_" 表示未使用变量的问题。它与国际化的唯一冲突是潜在的混淆,它不会真正与使用名为 "_" 的全局变量交互。然而,将 "_" 特殊用于此通配符模式目的仍然是有问题的:"_" 在模式匹配内部和外部的不同语义 _和含义_ 意味着 Python 的一致性出现了断裂。

引入 "?" 作为 _在模式匹配内部和外部_ 表示未使用变量的特殊语法,可以让我们保持这种一致性。它避免了与国际化 _或任何其他使用 _ 作为变量的用途_ 发生冲突。它使解包赋值与模式匹配更加一致,使模式匹配更容易被解释为解包赋值的扩展。

在代码可读性方面,使用特殊标记可以更容易地找出它的含义("what does question mark in Python do""why is my _ variable not getting assigned to"),并使实际意图更加明显,即该值不应该使用 - 因为它完全不可能使用。

规范

引入了一个新标记 "?"token.QMARK

语法修改为允许在赋值上下文中使用 "?"(在当前语法中为 star_atomt_atom),创建一个标识符设置为 NULL 的 Name AST 节点。

AST 修改为允许 Name 表达式的标识符可选(目前是必需的)。标识符为空只允许在 STORE 上下文中使用。

在 CPython 中,字节码编译器被修改为为没有标识符的 Name 节点发出 POP_TOP,而不是 STORE_NAME。其他使用 Name 节点的操作将根据需要进行更新,以处理标识符为空的情况。

修改后的语法节点的用法至少包含以下赋值形式

? = ...
x, ?, z = ...
x, *?, z = ...
for ? in range(3): ...  # including comprehension forms
for x, ?, z in matrix: ...  # including comprehension forms
with open(f) as ?: ...
with func() as (x, ?, z): ...

在非解包上下文中使用单个 "?" 被允许用于正常赋值和 with 语句。它本身并不真正有意义,并且可以禁止这些特定情况。但是,for ? in range(3) 显然有它的用途,因此出于一致性原因,如果没有任何其他原因,允许在其他情况下使用单个 "?" 似乎更有意义。

不允许在增强赋值中使用 "?"? *= 2),因为 "?" 只能用于赋值。多次出现 "?" 是有效的,就像为名称赋值时一样,并且赋值不会互相干扰。

向后兼容性

引入一个新标记意味着没有向后兼容性问题。没有有效的语法改变含义。

"?" 不被认为是标识符,因此 str.isidentifier() 不会改变。

AST 会以不兼容的方式改变,因为 Name 标记的标识符现在可以为空。使用 AST 的代码将需要进行相应的调整。

如何教授

可以与解包赋值一起引入 "?",解释它表示 "未使用" 的特殊语法,并提及它也可以在其他地方使用。或者,它可以作为对 for 循环中赋值的解释的一部分引入,展示循环变量未使用的示例。

PEP 636 讨论了如何教授 "_",可以简单地将 "_" 替换为 "?",也许可以指出 "?" 在其他上下文中也类似地可用。

参考实现

原型实现位于 <https://github.com/Yhg1s/cpython/tree/nonassign>。

被拒绝的想法

未解决的问题

在以下上下文中是否应该允许使用 "?"

# imports done for side-effect only.
import os as ?
from os import path as ?

# Function defined for side-effects only (e.g. decorators)
@register_my_func
def ?(...): ...

# Class defined for side-effects only (e.g. decorators, __init_subclass__)
class ?(...): ...

# Parameters defined for unused positional-only arguments:
def f(a, ?, ?): ...
lambda a, ?, ?: ...

# Unused variables with type annotations:
?: int = f()

# Exception handling:
try: ...
except Exception as ?: ...

# With blocks:
with open(f) as ?: ...

其中一些从一致性的角度来看似乎有意义,但实际用途有限且有疑义。对 "?" 的类型标注以及它与 exceptwith 的使用似乎没有任何意义。在参考实现中,except 不受支持(现有语法只允许使用名称),但 with 受支持(由于现有语法支持解包赋值)。

即使模式匹配被拒绝,本 PEP 也应该被接受吗?


来源: https://github.com/python/peps/blob/main/peps/pep-0640.rst

最后修改: 2023-09-09 17:39:29 GMT