Приоритет оператора Python с расширенным присваиванием
Кажется, на этот вопрос ответили только для Java, но я хотел бы знать, как это работает в Python. Так это одно и то же?
a += b / 2
а также
a += (b / 2)
2 ответа
Да, это то же самое. Расширенное назначение Python не является выражением, оно является утверждением и не играет в правилах приоритета выражений. +=
не является оператором, а вместо этого является частью расширенного синтаксиса оператора присваивания.
Так что все справа от+=
это выражение, но +=
само по себе это не так, поэтому назначение всегда будет обрабатываться последним.
А поскольку (расширенное) присваивание не является выражением, оно также не может создать значение для использования в окружающем выражении. Здесь нет (a += b) / 2
, это было бы синтаксической ошибкой, и, конечно, нет if (a += b / 2):
или другие подобные махинации.
См. Справочную документацию по расширенным операторам присваивания, в которой говорится:
augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression) augtarget ::= identifier | attributeref | subscription | slicing augop ::= "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|="
Итак augop
является частью синтаксиса оператора, и только следующая часть является выражением (в частности, либо expression_list
или же yield_expression
грамматическое правило).
Кроме того, объяснение показывает:
Расширенное назначение оценивает цель (которая, в отличие от обычных операторов назначения, не может быть распаковкой) и список выражений, выполняет двоичную операцию, специфичную для типа назначения для двух операндов, и присваивает результат исходной цели. Цель оценивается только один раз.
Итак augtarget
сначала обрабатывается part, затем обрабатывается список выражений (или yield yield), а затем расширенное присваивание применяет оператор и присваивает результат обратно.
Кроме того, справочная документация по выражениям включает в себя таблицу приоритетов, но эта таблица не включает в себя присваивания (дополненные или иные) просто потому, что присваивания - это не выражения, а операторы.
Краткий ответ: +=
является расширенным назначением, и если мы примем грамматику во внимание, это синтаксически анализируется в синтаксическом дереве, чем операторы в целом (и, следовательно, /
оператор в частности).
Питон видит +=
как "расширенное назначение". Если мы проверяем грамматику Python, мы видим:
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=')
Теперь грамматика также применяет правила приоритета при разборе. Если мы посмотрим на грамматику, которая связана с stmt
("утверждение") мы видим:
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
Мы можем иметь произвольное число 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
с операторами '*'
, '@'
, '/'
, '%'
, а также //
и вот мы наконец "потребляем" наш *
, Таким образом, это означает, что /
ниже в дереве синтаксиса, чем +=
узел.
Следовательно, Python анализирует это выражение так:
a += (b / 2)