Python equality statement of the form a==b in [c,d,e]
Question:
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
?
Answers:
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
.
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)
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.
For example, 9==9 in [1,2,3,9]
is confusingly the same as 9==9 and 9 in [1, 2, 3, 9]
.
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 of True
/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
.
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
?
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
.
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) |
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.
For example, 9==9 in [1,2,3,9]
is confusingly the same as 9==9 and 9 in [1, 2, 3, 9]
.
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
.