This is a bit of a (very basic) language-lawyer kind of question. I understand what the code does, and why, so please no elementary explanations.
In an expression, in
has higher precedence than and
. So if I write
if n in "seq1" and "something": ...
it is interpreted just like
if (n in "seq1") and "something": ...
However, the in
of a for
loop has lower precedence than and
(in fact it has to, otherwise the following would be a syntax error). Hence if a Python beginner writes
for n in "seq1" and "something": ...
..., it is equivalent to this:
for n in ("seq1" and "something"): ...
(which, provided "seq1" is truthy, evaluates to for n in "something"
).
So, the question: Where is the precedence of the for-loop's in
keyword specified/documented? I understand that n in ...
is not an expression in this context (it does not have a value), but is part of the for
statement's syntax. Still, I'm not sure how/where non-expression precedence is specified.
Answer: The correct order of precedence is given by PEMDAS which means Parenthesis (), Exponential **, Multiplication *, Division /, Addition +, Subtraction -.
Python will always evaluate the arithmetic operators first (** is highest, then multiplication/division, then addition/subtraction).
The new Assignment expression (:=) operator from Python 3.8 onwards has the lowest precedence while parentheses() have the highest precedence.
In the context of a for
statement, the in
is just part of the grammar that makes up that compound statement, and so it is distinct from the operator in
. The Python grammar specification defines a for
statement like this:
for_stmt ::= "for" target_list "in" expression_list ":" suite ["else" ":" suite]
The point to make is that this particular in
will not be interpreted as part of target_list, because a comparison operation (e.g. x in [x]
) is not a valid target. Referring to the grammar specification again, target_list and target are defined as follows:
target_list ::= target ("," target)* [","] target ::= identifier | "(" target_list ")" | "[" target_list "]" | attributeref | subscription | slicing | "*" target
So the grammar ensures that the parser sees the first in
token after a target_list as part of the for ... in ...
statement, and not as a binary operator. This is why trying to write things very strange like for (x in [x]) in range(5):
will raise a syntax error: Python's grammar does not permit comparisons like (x in [x])
to be targets.
Therefore for a statement such as for n in "seq1" and "something"
is unambiguous. The target_list part is the identifier n
and the expression_list part is the iterable that "seq1" and "something"
evaluates to. As the linked documentation goes on to say, each item from the iterable is assigned to target_list in turn.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With