Python 具有增强赋值的运算符优先级

Python operator precedence with augmented assignment

这个问题似乎只回答了 Java,但我想知道它在 Python 中是如何工作的。那么这些是一样的吗?

a += b / 2

a += (b / 2)

是的,它们是一样的。 Python 的扩充赋值 不是表达式 ,它是一个语句,不符合表达式优先级规则。 += 不是运算符,而是增强赋值语句语法的一部分。

所以 += 右边的所有 都是表达式,但 += 本身不是,所以赋值总是最后处理。

并且因为(增强的)赋值不是表达式,它也不能产生在周围表达式中使用的值。没有 (a += b) / 2,那是语法错误,当然也没有 if (a += b / 2): 或其他类似的恶作剧。

参见 reference documentation on Augmented assignment statements,其中声明语法为:

augmented_assignment_stmt ::=  augtarget augop (expression_list | yield_expression)
augtarget                 ::=  identifier | attributeref | subscription | slicing
augop                     ::=  "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**="
                           | ">>=" | "<<=" | "&=" | "^=" | "|="

所以augop是语句语法的一部分,只有后面的部分是表达式(具体来说,expression_listyield_expression语法规则)。

此外,解释显示:

An augmented assignment evaluates the target (which, unlike normal assignment statements, cannot be an unpacking) and the expression list, performs the binary operation specific to the type of assignment on the two operands, and assigns the result to the original target. The target is only evaluated once.

因此首先处理 augtarget 部分,然后处理表达式列表(或 yield 表达式),然后扩充赋值应用运算符并将结果赋值回来。

此外,表达式参考文档 does include a precedence table,但 table 不包括赋值(增强或其他),仅仅是因为赋值不是表达式而是语句。

简答+=是一个扩充赋值,如果我们考虑语法,这会被解析在语法树中高于一般运算符(因此特别是 / 运算符)。

Python 将 += 视为“ 扩充赋值 ”。如果我们检查 Python grammar,我们会看到:

augassign: (<b>'+='</b> | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
            '<<=' | '>>=' | '**=' | '//=')

现在语法在解析时也强制执行优先级规则。如果我们查看与 stmt ("statement") 相关的语法,我们会看到:

stmt: <b>simple_stmt</b> | compound_stmt
<b>simple_stmt</b>: <b>small_stmt</b> (';' small_stmt)* [';'] NEWLINE
<b>small_stmt</b>: (<b>expr_stmt</b> | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
<b>expr_stmt</b>: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*)

详尽解释所有其他陈述(如 del_statement)会花费太长时间,但 expr_stmt 是唯一导致 augassign(和 augassign 是导致 += 标记的唯一变量)。所以我们可以忽略其他表达式。

现在,如果我们 "specialize" expr_stmt 的表达式中有一个 augassign,我们将检索 生产规则

expr_stmt: testlist_star_expr augassign (yield_expr|testlist)

testlist_star_expr是一个变量,它会产生一个标识符(或者在序列解包的情况下会产生多个标识符)等

在右侧,我们看到 yield_exprtest_listtest_list 可以产生逗号分隔的表达式,其中:

testlist: test (',' test)* [',']

这个test允许写三元运算符,但是不是强制的:

test: or_test ['if' or_test 'else' test] | lambdef

我们可以使用 or_test 变量,它用于用 or 分隔符(同样可选)对表达式进行分组,因为 or 具有最高优先级。

or_test: and_test ('or' and_test)*

然后是 and_test,顾名思义,它允许我们编写 and 运算符:

and_test: not_test ('and' not_test)*

然后是 not 运算符(带有 not_test):

not_test: 'not' not_test | comparison

前面可以有任意数量的not,但最终我们会选择comparison

如果我们查看 comparison 的生产规则,我们会看到:

comparison: expr (comp_op expr)*

因此允许比较器链接,就像x <= y < z,接下来我们看一下expr

expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*

所以这定义了优先规则,我们看到 | 优先于 ^,优先于 &,等等,直到我们看到 term 是一个包含 '*''@''/''%'// 运算符的 factor 序列,所以这里我们最终 "consume"我们的*。因此,这意味着 / 在语法树中的位置低于 += 节点。

因此 Python 解析此表达式的方式是:

a += (b / 2)