简答:+= 是一个增广赋值,如果我们考虑语法,它在语法树中的解析比在一般(尤其是 / 运算符)。
Python 将+= 视为“增强赋值”。如果我们检查Python grammar,我们会看到:
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')
现在语法在解析时也强制执行优先级规则。如果我们查看与stmt ("statement") 相关的语法,我们会看到:
stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
详尽地解释所有其他语句(如del_statement)将花费太长时间,但expr_stmt 是唯一导致augassign 的语句(而augassign 是唯一导致+= 令牌)。所以我们可以忽略其他表达式。
现在,如果我们“特化”expr_stmt 的表达式,使其包含augassign,我们将检索生产规则:
expr_stmt: testlist_star_expr augassign (yield_expr|testlist)
testlist_star_expr 是一个变量,它产生一个标识符(或序列解包时的多个标识符)等。
在右侧,我们看到 yield_expr 或 test_list。 test_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
我们可以在前面有任意数量的nots,但最终我们会选择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 是factors 的序列'*'、'@'、'/'、'%' 和 // 的运算符,所以在这里我们最终“消费”了我们的 *。因此,这意味着/ 在语法树中低于+= 节点。
因此 Python 解析这个表达式的方式是:
a += (b / 2)