print('a' in 'aa')
print('a' in 'aa' == True)
print(('a' in 'aa') == True)
print('a' in ('aa' == True))
The output is
True
False
True
Traceback (most recent call last):
File "main.py", line 6, in <module>
print('a' in ('aa' == True))
TypeError: argument of type 'bool' is not iterable
If line 2 is neither line 3 nor line 4, then what is it? How does it get False?
According to Expressions
print('a' in 'aa' == True)
is evaluated as
'a' in 'aa' and 'aa' == True
which is False.
See
print("a" in "aa" and "aa" == True)
==> False
The rest is trivial - it helps to keep operator precedence in mind to figure them out.
Similar ones:
Multiple comparison operators in single statement
Why does the expression 0 < 0 == 0 return False in Python?
with different statements. I flagged for dupe but the UI is wonky - I answered non the less to explain why yours exactly printed what it did.
Case 1 : it's simple the answers is True.
print('a' in 'aa')
Case 2 : This operation is evaluated as 'a' in 'aa' and 'aa' == True, so obviously it will return false.
print('a' in 'aa' == True)
Case 3: Now because we have () enclosing ('a' in 'aa') and the precedence of () is highest among all so first 'a' in 'aa' is evaluated as True and then True == True
print(('a' in 'aa') == True)
Case 4 : Same as above because of precedence of (), its evaluated as 'aa' == True, which will result in error as it tries to apply in on a non iterable that is bool value.
print('a' in ('aa' == True))
Related
This question already has answers here:
Why does the expression 0 < 0 == 0 return False in Python?
(9 answers)
Closed 7 days ago.
The following comparisons produce True:
>>> '1' in '11'
True
>>> ('1' in '11') == True
True
And with the parentheses the other way, I get a TypeError:
>>> '1' in ('11' == True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'bool' is not iterable
So how do I get False with no parentheses?
>>> '1' in '11' == True
False
The Python manual says in and == are of equal precedence. Thus, they're evaluated from left to right by default, but there's also chaining to consider. The expression you put above ('1' in '11' == True) is actually being evaluated as...
('1' in '11') and ('11' == True)
which of course is False. If you don't know what chaining is, it's what allows you to do something like...
if 0 < a < 1:
in Python, and have that mean what you expect ("a is greater than 0 but less than 1").
It has nothing to do with precedence. In Python relational operators chain, and containment is considered a relational operator. Therefore:
'1' in '11' == True
is the same as:
('1' in '11') and ('11' == True)
which is false since True is not equal to "11".
Chaining allows you to write x < y < z, and mean x < y and y < z. Look at this interaction:
>>> (False == True) == False
True
>>> False == (True == False)
True
>>> False == True == False
False
>>>
So in your example, '1' in '11' == True is equivalent to ('1' in '11') and ('11' == True)
I don't see why the following expression evaluates to False:
>>> 1 in [1,2,3] == True
False
in and == has the same precedence and groups left to right, but:
>>> (1 in [1,2,3]) == True
True
and
>>> 1 in ([1,2,3] == True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'bool' is not iterable
Where is the problem with the parenthesis?
Comparison operators at the same level "chain". You may have seen something such as 10 < x < 25, which expands to (10 < x) and (x < 25).
Similarly, your original expression expands to
(1 in [1,2,3]) and ([1,2,3] == True)
The first expression evaluates as True, as you already know. The second expression returns False, giving the result you see.
Can anyone explain this behaviour in Python (2.7 and 3)
>>> a = "Monday" and "tuesday"
>>> a
'tuesday' # I expected this to be True
>>> a == True
False # I expected this to be True
>>> a is True
False # I expected this to be True
>>> a = "Monday" or "tuesday"
>>> a
'Monday' # I expected this to be True
>>> a == True
False # I expected this to be True
>>> a is True
False # I expected this to be True
I would expect that because I am using logic operators and and or, the statements would be evaluated as a = bool("Monday") and bool("tuesday").
So what is happening here?
As explained here using and / or on strings will yield the following result:
a or b returns a if a is True, else returns b.
a and b returns b if a is True, else returns a.
This behavior is called Short-circuit_evaluation and it applies for both and, or as can be seen here.
This explains why a == 'tuesday' in the 1st case and 'Monday' in the 2nd.
As for checking a == True, a is True, using logical operators on strings yields a specific result (as explained in above), and it is not the same as bool("some_string").
When I was looking at answers to this question, I found I didn't understand my own answer.
I don't really understand how this is being parsed. Why does the second example return False?
>>> 1 in [1,0] # This is expected
True
>>> 1 in [1,0] == True # This is strange
False
>>> (1 in [1,0]) == True # This is what I wanted it to be
True
>>> 1 in ([1,0] == True) # But it's not just a precedence issue!
# It did not raise an exception on the second example.
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
1 in ([1,0] == True)
TypeError: argument of type 'bool' is not iterable
Thanks for any help. I think I must be missing something really obvious.
I think this is subtly different to the linked duplicate:
Why does the expression 0 < 0 == 0 return False in Python?.
Both questions are to do with human comprehension of the expression. There seemed to be two ways (to my mind) of evaluating the expression. Of course neither were correct, but in my example, the last interpretation is impossible.
Looking at 0 < 0 == 0 you could imagine each half being evaluated and making sense as an expression:
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True
So the link answers why this evaluates False:
>>> 0 < 0 == 0
False
But with my example 1 in ([1,0] == True) doesn't make sense as an expression, so instead of there being two (admittedly wrong) possible interpretations, only one seems possible:
>>> (1 in [1,0]) == True
Python actually applies comparison operator chaining here. The expression is translated to
(1 in [1, 0]) and ([1, 0] == True)
which is obviously False.
This also happens for expressions like
a < b < c
which translate to
(a < b) and (b < c)
(without evaluating b twice).
See the Python language documentation for further details.
def test_string_membership():
assert False == 'c' in 'apple'
assert True == 'a' in 'apple'
assert True == 'app' in 'apple'
p.s:- I am a beginner in python and unable to find out whats wrong. My assertion fails when I run the code.
False == 'c' in 'apple' is not interpreted as
False == ('c' in 'apple')
but,
(False == 'c') and ('c' in apple)
becaue of comparison chaining.
To get what you want, put parentheses explicitly.
False == ('c' in 'apple')
or more preferably use in / not in:
def test_string_membership():
assert 'c' not in 'apple'
assert 'a' in 'apple'
assert 'app' in 'apple'
You have a problem with comparison chaining, the Python syntax that treats:
x < y < z
as:
x < y and y < z.
In your case, that means the expression False == 'c' in 'apple' is being treated as:
(False == 'c') and ('c' in 'apple')
both of which are false, hence causing the assertion. Details on comparison chaining for Python 3 can be found here.
So the way to avoid this chianing is to make the expression explicit, with something like:
assert False == ('c' in 'apple')
assert True == ('a' in 'apple')
assert True == ('app' in 'apple')
or, even better, since comparing with true/false is rarely a good idea:
assert 'c' not in 'apple' # or "not('c' in 'apple')" if you're testing 'in'.
assert 'a' in 'apple'
assert 'app' in 'apple'
Contrary to the other answers, what is happening here is not operator precedence but comparison chaining. a == b in c means (a == b) and (b in c), just like a < b < c means (a < b) and (b < c). However, in either case, the upshot is the same, which is that it's not what you meant to do. As noted in the other answers and comments, it can be fixed by using parentheses, or, better, by not using an equality comparison at all and just doing assert 'c' not in 'apple'.
You can see that this is comparison chaining by a slightly different example:
>>> 'a' == 'a' in 'ab'
True
This would obviously be false no matter which way the precedence went, but it is true because 'a' == 'a' and 'a' in 'ab' are both true.
Using parenthesis should solve this as in
def test_string_membership():
assert False == ('c' in 'apple')
assert True == ('a' in 'apple')
assert True == ('app' in 'apple')
You may use () in this case. There are better ways to do what you are trying.
def test_string_membership():
assert False == ('c' in 'apple')
assert True == ('a' in 'apple')
assert True == ('app' in 'apple')
This is because of precedence. Read more about this on Python docs.
in, not in, is, is not, <, <=, >, >=, <>, !=, == are in the same precedence level. So Python will evaluate
False == 'c' in 'apple'
from left to right.