“软关键词”是什么?

114
根据关键字模块的文档,Python 3.9中添加了两个新成员:
  • issoftkeyword
  • softkwlist
然而,它们的文档没有透露任何关于它们目的的信息。这个变化甚至在新特性文章中都没有提到,而通常所有API更改都会被记录。深入挖掘源代码最终导致这个拉取请求,其中提到"这基本上是一个内部工具",并且"soft keywords仍未使用"。那么Python的soft keywords有什么目的?

软关键字在PEP 634中提到。 - khelwood
3个回答

76

简述:软关键字仍可用作变量或参数名。

PEP 622给出了解释(重点标识自己):

硬关键字和软关键字的区别在于,硬关键字总是保留字,即使在它们不起作用的位置(例如,x = class + 1),而软关键字只有在上下文中才具有特殊含义。

提议将match和case关键字设置为软关键字,以便它们在匹配语句或case块开头被识别为关键字,但允许在其他地方用作变量或参数名


4
甚至包括 PEP 634,该文替代了622。 - khelwood
6
PEP 634包含相同的示例(matchcase),但没有提供软关键字的一般解释。PEP 622提供了这个解释。 - couka
2
在Python 2中,TrueFalse根本不是关键字:它们只是内置作用域中的标识符。 asNone都不是有效的标识符名称。 (至少,我认为as从一开始就是import语句的一部分,因此它是一个关键字。如果它曾经是一个有效的标识符,那么它是在Python 1.5之前的某个时候。) - chepner
5
请参考我于 2003 年提交的 这个问题。当时,将变量赋值为 None 明确会引发 SyntaxWarning 警告信息,而在 Python 2.5 及之前版本中,将变量赋值为 as 同样也会出现警告信息。 - gerrit
3
在C++和C#中也称为“上下文关键字”,在C++/CLI中被称为“上下文敏感关键字”。 - phuclv
显示剩余5条评论

39

我认为最好用演示来解释。在Python 3.5和3.6中,asyncawait是软关键字,因此它们可以被用作标识符:

>>> async = "spam"
>>> async def foo():
...     pass
...
>>> await = "bar"
>>> async, await
('spam', 'bar')

但在Python 3.7中,它们成为了适当的关键字,只能在特定的上下文中使用,其中它们是有意义的:

>>> async = "123"
  File "<stdin>", line 1
    async = "123"
          ^
SyntaxError: invalid syntax
>>> async def foo():
...     pass
...
>>> await = "bar"
  File "<stdin>", line 1
    await = "bar"
          ^
SyntaxError: invalid syntax
>>> async, await
  File "<stdin>", line 1
    async, await
         ^
SyntaxError: invalid syntax

最初将它们作为软关键字引入的想法主要是为了不破坏现有使用它们作为标识符的代码。同样的理由也适用于即将到来的 match 关键字,否则会完全破坏例如 re.match 和数百万个项目。


1
事实上,我当时忘记了asyncawait是软关键字(虽然它们没有包含在keyword模块中)。我有印象是新的PEG解析器使软关键字成为可能,那么你知道他们在Python 3.6中如何实现asyncawait吗? - a_guest
2
它们是通过a tokenizer (lexer) hack实现的。Tokenizer执行前瞻并维护有关是否在函数内部的上下文,而不是解析器,并返回异步和等待的特殊标记。该解决方案无法处理在异步函数之外定义的异步推导(在3.6中无效,但在3.7中有效),并且对于未来的软关键字也不是可扩展的解决方案。 - bgw

23

软关键字是上下文敏感的关键字。例如,只要它不能被解释为定义类,就可以将 class 用作变量名。例如,可以将 cls 替换为 class

今天这是不可能的,因为 class 是一个关键字:

>>> def a(class):
  File "<stdin>", line 1
    def a(class):
          ^

从上下文来看,很明显该用户并不是想要定义一个新类,而是想要一个名为 class 的标识符。


10
更重要的是,它同样也能够实现相反的效果:在不影响现有代码中变量名的使用的情况下,让你添加一个关键字到语言中。 ":=" 赋值运算符存在的一个原因是因为没有达成对于重复使用现有关键字 ("as") 的合适协议。如果我来选择,“let x = 3 in x * 2” 是我的个人偏好,而不是 “(x:=3) + 2”,但是由于增加新关键字可能导致破坏现有代码的风险很高,所以极难新增关键字。 - chepner
(要明确的是,我不确定是否明确考虑了这样的“let”表达式,但新关键字方面的问题至少与向“in”关键字添加另一个含义一样重要。) - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接