This is regarding python language.I have observed that 'a' in 'apple' returns True.But why assert True == 'a' in 'apple' is raising an assertion error.
-
There's no need to write `assert True == ('a' in 'apple')` when `assert 'a' in 'apple'` will do just fine. – jme Jan 21 '15 at 15:36
-
possible duplicate of [Odd operator precedence/associativity behaviour](http://stackoverflow.com/questions/28071165/odd-operator-precedence-associativity-behaviour) – georg Jan 21 '15 at 16:12
-
@georg, dups are usually asked after the question they are a dupe of. That question came about because of this question. – Padraic Cunningham Jan 21 '15 at 16:13
-
@PadraicCunningham: I saw that one first, that's why. – georg Jan 21 '15 at 16:20
4 Answers
From the Python Docs:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z [...]
in
and ==
are both comparators, so the code you wrote does two comparisons: True == 'a'
and 'a' in 'apple'
. The first of those is incorrect.

- 737
- 6
- 11
NOTE: This answer has taken me on a fun ride through the semantics of Python chained conditionals. Rather than remove my initial incorrect answer, I've preserved my chain of research as a series of EDIT statements below it. For the real answer you can skip to the bottom, read the comments, or check out several of the other worthy answers to this question.
Because of the rules of operator precedence. In Python, the ==
operator and the in
operator have the same precedence, so they are evaluated in left-to-right order. In other words, you have written the equivalent of assert( (True == 'a') in 'apple')
. Since True != 'a'
, you are asserting False in 'apple'
, which is also false, and the assertion fails.
Boolean comparisons with True
and False
are redundant and can be eliminated. A more concise way to say the exact same thing is:
assert 'a' in 'apple'
EDIT: It has been pointed out below that assert( (True == 'a') in 'apple' )
actually raises a different exception (TypeError
). I tried to understand this with the following disassembly:
>>> def orig(): ... assert True == 'a' in 'apple' ... >>> def paren(): ... assert( (True == 'a') in 'apple') ... >>> import dis >>> dis.dis(orig) 2 0 LOAD_GLOBAL 0 (True) 3 LOAD_CONST 1 ('a') 6 DUP_TOP 7 ROT_THREE 8 COMPARE_OP 2 (==) 11 JUMP_IF_FALSE_OR_POP 23 14 LOAD_CONST 2 ('apple') 17 COMPARE_OP 6 (in) 20 JUMP_FORWARD 2 (to 25) >> 23 ROT_TWO 24 POP_TOP >> 25 POP_JUMP_IF_TRUE 34 28 LOAD_GLOBAL 1 (AssertionError) 31 RAISE_VARARGS 1 >> 34 LOAD_CONST 0 (None) 37 RETURN_VALUE >>> dis.dis(paren) 2 0 LOAD_GLOBAL 0 (True) 3 LOAD_CONST 1 ('a') 6 COMPARE_OP 2 (==) 9 LOAD_CONST 2 ('apple') 12 COMPARE_OP 6 (in) 15 POP_JUMP_IF_TRUE 24 18 LOAD_GLOBAL 1 (AssertionError) 21 RAISE_VARARGS 1 >> 24 LOAD_CONST 0 (None) 27 RETURN_VALUE >>>
What this actually shows is that Python has interpreted the comparison operators (==
and in
) as a chain that can fast-fail to False, causing the AssertionError
. As soon as True == 'a'
is evaluated to False, Python deduces that the entire statement must be false, and triggers the assertion failure without continuing to attempt to evaluate False in 'apple'
. When I explained earlier by adding parentheses, I forced the interpreter to evaluate each nested parenthetical in order, so it could not use short-circuit evaluation and avoid the later exception.
EDIT 2: It seems my short-circuit-evaluation hypothesis was incomplete, as well. The real answer lies in the way that Python treats chained conditionals: as a series of individual boolean comparisons joined by boolean and
. Here's a walkthrough of the bytecode I disassembled above, using the Python prompt to represent the interpreter's stack as a list:
>>> stack = [] # The empty stack >>> stack.append(True) # LOAD_GLOBAL 0 (True) >>> stack.append('a') # LOAD_CONST 1 ('a') >>> stack.append(stack[-1]) # DUP_TOP >>> stack [True, 'a', 'a'] >>> stack[-3],stack[-2],stack[-1] = stack[-1], stack[-3], stack[-2] # ROT_THREE >>> stack ['a', True, 'a'] >>> stack.append(stack[-2] == stack[-1]) >>> stack ['a', True, 'a', False] >>> # JUMP_IF_FALSE_OR_POP 23
At this point, were the first condition (True == 'a'
) true, we would pop the True
value off the top of the stack and continue evaluating whether the next value ('a'
) were in 'apple'
(lines 14 and 17). This is what we should expect based on the fact that True == 'a' in 'apple'
is equivalent to (True == 'a') and ('a' in 'apple')
. The JUMP_IF_FALSE_OR_POP
is what implements the short-circuit evaluation of a boolean and
chain: as soon as one false condition is encountered, the entire expression must be false.
The JUMP_FORWARD 2 (to 25)
on line 20 means that the two branches converge on line 25, which is where the final result of the chained conditional is examined. Let's pick back up with the real chain of execution, the jump to 23 based on the False
result:
>>> stack[-2], stack[-1] = stack[-1], stack[-2] # ROT_TWO >>> stack ['a', True, False, 'a'] >>> stack.pop() # POP_TOP 'a' >>> stack ['a', True, False] >>> stack.pop() # POP_JUMP_IF_TRUE 34 False
Now we reach another conditional jump, but we don't take it because the result was False
, not True
. Instead, we continue with lines 28 and 31, which prepare and raise the AssertionError
.
One final note: As the documentation for the dis
library notes, the bytecode is an implementation detail of CPython that is not guaranteed to be the same between versions. I am using Python 2.7.3.

- 5,825
- 1
- 20
- 35
-
3
-
4This is not correct. They are not evaluated left-to-right, but the same way as e.g. `x < y < z`, i.e. as `(True == 'a') and ('a' in 'apple')` The first part is `False`, so the assertion fails. – tobias_k Jan 21 '15 at 15:39
-
@NPE Thanks for pointing that out. I dug further and found exactly what tobias_k said in his comment. See my edit. – bonsaiviking Jan 21 '15 at 16:00
-
@tobias_k Thanks for pointing that out! I addressed it in my edit, and learned something in the process. – bonsaiviking Jan 21 '15 at 16:02
-
4Your edit is still incorrect. The issue here is not short-circuiting, but that the chained comparisons are evaluated as `(True == 'a') and ('a' in 'apple')` (It would not have tried to evaluate `False in 'apple'` as you claim, even if there was no short-circuiting). – interjay Jan 21 '15 at 16:17
-
@interjay Thanks for pushing me to do better. My last edit should cover all the relevant points, and admits the fault in my earlier hypotheses. – bonsaiviking Jan 22 '15 at 04:00
Operator precedence is the cause. Try this:
# Python 2.7
assert True==('a' in 'apple')
That should not raise an assertion error. What you were attempting to do was assert that the True
singleton is equal to the string a
, which evaluates to False
.
Having said that, if you are actually using this assertion for testing, there's no need to compare the result with True
. Simply:
assert 'a' in 'apple'

- 5,150
- 1
- 15
- 25
The comparising operators in in
and ==
have the same precedence (https://docs.python.org/2/reference/expressions.html#operator-precedence) and the statement is evaluated from left to right, evaluating every binary link by itself. An equivalent expression would be True == 'a' and 'a' in 'apple'
(https://docs.python.org/3.5/reference/expressions.html#comparisons) and therefore you are testing for False And True
which is false.

- 9,713
- 6
- 25
- 41