I just came across some python code with the following statement:
if a==b in [c,d,e]:
...
It turns out that:
>>> 9==9 in [1,2,3]
False
>>> 9==9 in [1,2,3,9]
True
>>> (9==9) in [1,2,3,9]
True
>>> 9==(9 in [1,2,3,9])
False
>>> True in [1,2,3,9]
True
>>> True in []
False
>>> False in []
False
>>> False in [1,2,3]
False
Am I right in assuming that a==b in [c,d,e]
is equivalent to (a==b) in [c,d,e]
and therefore only really makes sense if [c,d,e]
is a list of True/False values?
And in the case of the code I saw b
is always in the list [c,d,e]
. Would it then be equivalent to simply using a==b
?
CodePudding user response:
Am I right in assuming that
a==b in [c,d,e]
is equivalent to(a==b) in [c,d,e]
No. Since both ==
and in
are comparison operators, the expression
a == b in [c, d, e]
is equivalent to
(a == b) and (b in [c, d, e])
since all comparison operators have the same precedence but can be chained.
and therefore only really makes sense if
[c,d,e]
is a list ofTrue
/False
values?
It can also make sense to check if a boolean value is contained in a list of integers. Since True
is considered equivalent to 1
, and False
is considered equivalent to 0
(see The standard type hierarchy), the result of this check can even be True
.
CodePudding user response:
Just to throw another factor into the mix, operator chaining needs to be kept in mind as well:
Formally, if a, b, c, …, y, z are expressions and op1, op2, …, opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once.
The docs there specify "comparison operators", but later down is the addition:
Note that comparisons, membership tests, and identity tests, all have the same precedence and have a left-to-right chaining feature as described in the Comparisons section.
9==9 in [1,2,3,9]
is confusingly the same as 9==9 and 9 in [1, 2, 3, 9]
.
CodePudding user response:
It seems like what is happening here, is that python interprets the values 1, 1.0, True
all as equal in this case. So as long as the 1
is present in the list, the in
operator returns True
when comparing against a statement that compiles to True
.
CodePudding user response:
a == b in [c,d,e]
means ( a == b ) and (b in [c,d,e])
True in [1,2,3]
means 1 in [1,2,3]
(True == 1
)
False in [0]
means 0 in [0]
(False == 0
)
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
Formally, if a, b, c, …, y, z are expressions and op1, op2, …, opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once.
Note that a op1 b op2 c doesn’t imply any kind of comparison between a and c, so that, e.g., x < y > z is perfectly legal (though perhaps not pretty).
The new Assignment expression (:=) operator from Python 3.8 onwards has the lowest precedence while parentheses() have the highest precedence.
Operator | Description |
---|---|
:= | Assignment expression (Lowest precedence) |
lambda | Lambda expression |
if-else | Conditional expression |
or | Boolean OR |
and | Boolean AND |
not x | Boolean NOT |
<, <=, >, >=, | Comparison operators |
!=, == | Equality operators |
in, not in, is, is not, | Identity operators, membership |
| | Bitwise OR |
^ | Bitwise XOR |
& | Bitwise AND |
<<, >> | Left and right Shifts |
, – | Addition and subtraction |
*, @, /, //, % | Multiplication, matrix multiplication, division, floor division, remainder |
x, -x, ~x | Unary plus, Unary minus, bitwise NOT |
** | Exponentiation |
await x | Await expression |
x[index], x[index:index], x(arguments…), x.attribute | Subscription, slicing, call, attribute reference |
(expressions…), [expressions…],{key: value…}, {expressions…} | Binding or parenthesized expression, list display, dictionary display, set display |
() | Parentheses (Highest precedence) |