Python 无法将非运算符绑定到一元操作数

Python fails to bind not operator to unary operand

在 Python 中,如果 'not' 运算符跟在按位运算符(例如“&”或“|”)之后,结果是语法错误。当然,这将是对二进制值的按位运算,但这应该没问题。据我所知,C 没有问题。

例如,这个有效:

a = 0
b = 1
anot = not(a)
bnot = not(b)
c = anot | bnot

但这失败了:

c = not(a) | not(b)

这些工作:

c = not(a) | (not(b))   
c = not a | (not b)  

谁能告诉我为什么会这样?不寻找解决方法,只是对实现的解释。与此同时,我会努力浏览源代码和 CFG,看看是否能学到更多。到目前为止,我还没有在 Stacks 或其他谷歌上发现任何类似的问题。谢谢!

请记住,not 是关键字,不是函数。所以表达式 not(a) 在语义上等同于 not a。前两个示例中的括号对绑定运算符没有任何帮助。但是第三个例子中的 (not a) 会强制先计算内部表达式。

Python 语法确实清楚地表明了发生了什么:(我编辑了长长的不同比较运算符列表,除了 non-terminal 名称和运算符本身之外,它们都是相同的)

inversion:
    | 'not' inversion 
    | comparison
comparison:
    | bitwise_or compare_op_bitwise_or_pair+ 
    | bitwise_or
compare_op_bitwise_or_pair:
    | eq_bitwise_or
    # ...
eq_bitwise_or: '==' bitwise_or
# ...
bitwise_or:
    | bitwise_or '|' bitwise_xor 
    | bitwise_xor
bitwise_xor:
    | bitwise_xor '^' bitwise_and 
    | bitwise_and
bitwise_and:
    | bitwise_and '&' shift_expr 
    | shift_expr

因此,not 的操作数必须是 comparison,或者从 comparison 开始的优先级链中的某项。 | 的操作数必须是 bitwise_or(右侧的 bitwise_xor)或它们的优先级链下的操作数。由于 bitwise_ornot 更靠下,因此 bitwise_or 表达式可以是 not 的操作数,但 not 表达式不能是 not 的操作数|.

所以not 0 | 1表示not (0 | 1),因为0 | 1可以是not的操作数,而not 0不能是|的操作数. 0 | not 1 是语法错误,因为 not 1 不能是 | 的操作数,并且没有其他解析表达式的方法。

请注意,这与 C 不同。在 C 中,所有一元前缀运算符的绑定比任何二元运算符都更紧密,因此 !0|1 表示 (!0) | 1,即 1。这与 Python 表达式 not 0 | 1 相反,即 False

当然,这并不能解释为什么 Python语法是这样写的,我无法给出完整的历史记录的推理。显然,

被认为是可取的
    not a < b

意思是not (a < b),而不是(not a) < b。很少需要后一种解释,因此它具有一定的意义。此外,这与其他布尔运算符的工作方式一致; a < b and b < c 实际上意味着天真的 reader 可能会期望的内容。在 C 语言中也是如此:a < b && b < c 不需要加括号来提供预期的解析。 (但请注意,在 C 中,&| 与 Python 的同名运算符在优先级列表中的位置不同。)

所以这一切都有些可以理解,但问题是为什么要写语法以禁止像1 | not a这样的无歧义表达式,无论优先级如何,它只能以一种方式进行解析。在这里,我只能猜测。

当然,可以编写一个允许像那样明确表达的语法。但这并不容易,如果您将自己限制在简单的 BNF(或者甚至 Python 语法中现在使用的扩展 BNF 变体)。问题是级联优先样式不允许循环;如果优先级没有形成一致的偏序,解析器会报告歧义。另一方面,如果您使用 Yacc/Bison-like 解析器生成器,或者您可以通过搜索该短语找到的许多 operator-precedence 解析技术中的任何一种,那么它一点也不难。因此,决定使用没有 precedence-based 消歧的解析器生成器的决定可能与实现有关。

您 运行 使用较低优先级的一元运算符的歧义如下,人们通常 运行 在尝试为包含 let 的语言编写语法时遇到这种歧义表达式:"let" <var> "=" <expr> "in" <expr>。在该构造中,第二个 <expr> 是贪婪的:它会扩展到它可以扩展的范围。但是没有明显的理由说明 let 表达式本身在运算符的 right-hand 端不应该是合法的:

z = 3 * let x = 6 in x - 1/6

let 表达式的计算结果为 29/6 (6 - (1 / 6)),因此有充分的理由相信 z 将是 14.5,而不是解析器报告语法错误。但是,使用 naively-written 语法,您要么得到语法错误,要么得到一些奇怪的歧义报告。当语法以与 Python 实现 not 相同的方式实现 let 时出现语法错误,并且出于相同的原因:let 表达式不能是 [ 的操作数=55=],两边。

如果您尝试修改级联优先语法以允许 let* 的 right-hand 一侧,您通常会遇到新的歧义;当解析器到达 - 时,它可以选择终止 乘法 ( 3 * let x = 6 in x) - 1/6 或让 let 吸收表达式的其余部分,3 * (let x = 6 in x - 1/6)。我不认为大多数人 reader 会期待第一次解析,虽然你永远不知道,但解析器不会根据人类的直觉进行操作。 (这通常是一件好事。)

这对于 operator-precedence 解析器来说是微不足道的,因为您需要做的就是定义 let,左边的优先级最高,右边的优先级最低。 let 运算符本身停留在解析器堆栈上,直到解析器被迫将其弹出,因为它到达表达式的末尾或右括号,这有效地“隐藏”了 * 的优先级操作员。因此,一切都按预期工作。