【问题标题】:Python operator precedence with augmented assignment具有增强赋值的 Python 运算符优先级
【发布时间】:2019-03-01 11:33:35
【问题描述】:

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

a += b / 2

a += (b / 2)

【问题讨论】:

  • 简短回答:是的
  • += 不是“纯”运算符,它是一个赋值。它只允许前面有一个变量名,因此有一种直接的解释方法。
  • 你试过了吗?对我来说,它看起来像(经过测试)。
  • 是的,它们是(因为+= 中有一个作业)。看看'evaluation order' and 'operator precedence'
  • 是的。与 Java 不同,赋值(= 以及 +=,...)不是 Python 中的表达式,所以 (a += b) / 2 无论如何都会是一个错误。

标签: python operator-precedence augmented-assignment


【解决方案1】:

简答+= 是一个增广赋值,如果我们考虑语法,它在语法树中的解析比在一般(尤其是 / 运算符)。

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_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

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

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

comparison: expr (comp_op expr)*

因此允许比较器链接,比如x &lt;= y &lt; 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)*

所以这定义了优先规则,我们看到| 优先于^,它优先于&amp;,依此类推,直到我们看到termfactors 的序列'*''@''/''%'// 的运算符,所以在这里我们最终“消费”了我们的 *。因此,这意味着/ 在语法树中低于+= 节点。

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

a += (b / 2)

【讨论】:

    【解决方案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 语法规则)。

    此外,解释显示:

    扩充赋值计算目标(与普通赋值语句不同,它不能解包)和表达式列表,对两个操作数执行特定于赋值类型的二元运算,并将结果分配给原始目标.目标只评估一次。

    所以首先处理augtarget部分,然后处理表达式列表(或yield表达式),然后扩展赋值应用运算符并返回结果。

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

    【讨论】:

      猜你喜欢
      • 2019-03-01
      • 2013-03-27
      • 1970-01-01
      • 2015-08-24
      • 2020-03-22
      • 2015-11-14
      • 1970-01-01
      • 2012-12-09
      • 1970-01-01
      相关资源
      最近更新 更多