为什么 'not(True) * True' 是有效的语法,而 'True * not(True)' 不是?

21

这是否是Python操作符层次结构的后果?

not(True) * True
# False
True * not(True)
# SyntaxError: invalid syntax

22
你的括号放错了位置。第一行应该是(not True) * True,第二行应该是True * (not True)。在第二行中,你正在将Truenot相乘,这是一个语法错误。not不是函数,所以在它后面不需要使用圆括号。 - MattDMo
8
注意:括号是一个干扰项。True * not True 会产生相同的语法错误。 - John Kugelman
1
@John 这里可能会出现哪些类型不匹配?这里涉及的唯一对象是布尔值。 - wjandrea
2
@mpez0 对的...运算符不是一个对象,因此它本身没有类型。 - wjandrea
1
为什么有人要对布尔值进行乘法运算? - Thomas Weller
显示剩余4条评论
2个回答

34

这是 Python 中运算符的层级顺序导致的后果吗?

是的(尽管通常术语是 运算符优先级)。简而言之:

  1. not isn't a function; it's an operator. Therefore, we don't need to write parentheses for not (True), and in fact they don't do anything here. All that happens is that the parentheses are treated as ordinary grouping parentheses - (True) is evaluated before anything else, becoming True. So, let's consider the examples without the parentheses.

  2. not True * True means not (True * True). It does not mean (not True) * True, due to operator precedence. This is by design:

    >>> not 1 * 0
    True
    >>> not (1 * 0)
    True
    >>> (not 1) * 0
    0
    

    It would, the developers figured, be unexpected to write something like not 1 * 0 and get an integer result, and unexpected to write not in front of a mathematical operation and have the not only apply to the first thing in that expression.

  3. Because of that same operator precedence, True * not True is a syntax error. Python parses the not by itself as the right-hand side of the *, because it hasn't yet worked out to put not True together. True * not is obviously nonsense. Or, another way of looking at it: "not followed by an expression" isn't in the list of "things that can be an operand for *".

    This is perhaps surprising because the other commonly used unary operator, - (i.e., unary negation), doesn't have this issue. But that's because the precedence is the other way around: unary negation is processed before multiplication, not after.

    The same is true for and and or combinations:

    >>> 3 * 5 and 1 # 3 * 5 is evaluated first
    1
    >>> 3 * (5 and 1)
    3
    >>> 3 or 1 * 5 # 1 * 5 is evaluated first, even though it comes later
    3
    >>> (3 or 1) * 5
    15
    

5
优先级较低的一元运算符有些让人摸不着头脑。 - Yakk - Adam Nevraumont
1
@Yakk-AdamNevraumont 的确。我所知道的其他语言在这种情况下不会给出语法错误,它们只会计算出另一个(意外的)值。 - A.H.
5
看起来这个答案可能犯了一个错误,认为优先级控制了求值顺序。优先级并不强制求值顺序,它只是控制了什么作为什么的参数。例如,在 a + b + c * d 中,你可以通过重载操作符看到,在求值时先计算 a + b,再计算 c * d - user2357112
1
分组括号并不会强制先计算它们的内容,True * not True 的解析失败也不是因为它还没有机会应用 not True。(not后跟一个表达式"不在"可以成为"*"操作数的事物列表"的部分是正确的。) - user2357112
2
@user2357112,嗯,优先级控制了依赖关系下的评估顺序 - 类似于在调用函数之前必须对其参数进行评估。对于初学者来说,要精确而又清晰是很困难的。但你说得很好。我会稍后再审查一下。至于“没有机会”的具体措辞,我会尽量想出更好的表达方式。 - Karl Knechtel
我没有很多好的想法,但我认为这可以解决它。 - Karl Knechtel

12

这是一个优先级的问题,以及如何实现优先级。

*的优先级比not更高,在Python语法中的实现方式是,有一种表达式类型的层次结构,使得高优先级的运算符可以成为低优先级运算符的参数“根”,但反过来则不行。

例如,乘法表达式的文法规则为:

term[expr_ty]:
    | a=term '*' b=factor { _PyAST_BinOp(a, Mult, b, EXTRA) }
    | a=term '/' b=factor { _PyAST_BinOp(a, Div, b, EXTRA) }
    | a=term '//' b=factor { _PyAST_BinOp(a, FloorDiv, b, EXTRA) }
    | a=term '%' b=factor { _PyAST_BinOp(a, Mod, b, EXTRA) }
    | a=term '@' b=factor { CHECK_VERSION(expr_ty, 5, "The '@' operator is", _PyAST_BinOp(a, MatMult, b, EXTRA)) }
    | factor

term是用于乘法表达式的语法规则。此规则中的前5个选项都由一个乘法优先级运算符位于中间,另一个term位于运算符左侧,以及一个factor位于右侧组成,其中factor是下一个更高优先级运算符类别的规则。第6个选项只是一个factor

像这样构建语法规则可以确保解析出来的语法树始终与操作符优先级给出的结构相匹配,但也意味着低优先级的操作符不能成为高优先级操作符参数的“根”,即使表达式似乎不含歧义也无法这样做。没有语法规则能允许not表达式作为*表达式的参数。

(大多数表达式的语法规则遵循上述结构,但也有例外情况。例如,用于括号的语法规则不遵循“高优先级表达式内部不包含低优先级运算符”的结构,这就是为什么你可以写像3 * (4 + 5)这样的表达式。指数运算也是一种例外 - **在左边比一元的+/-/~运算符更紧密地绑定,但在右边不是,因此**和一元+/-/~的规则不遵循明确的优先级层次结构。)


其他一元运算符(+x-x~x)的优先级低于指数运算符** - 因此应该在这里看到类似的效果:42 ** ~ 2。但是这个表达式确实有效。所以我闻到了另一个特殊情况 :-) - A.H.
@A.H.:是的,我在上一段中提到了。这些一元操作符并不严格比 ** 低优先级。在 运算符优先级表 下甚至有一个脚注指出了 ** 在每侧的优先级略有不同。在 语法规则 中,您可以看到这是通过在 RHS 上使 power 接受 factor 而不是 power 来实现的。(factor 是一元 +/-/~ 的规则。) - user2357112

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