9

I just stumbled upon the following line in Python 3.

1 in range(2) == True

I was expecting this to be True since 1 in range(2) is True and True == True is True.

But this outputs False. So it does not mean the same as (1 in range(2)) == True. Furthermore it does not mean the same as 1 in (range(2) == True) which raises an error.

Despite years of experience in Python, I am taken off guard. What is going on?

Boann
  • 48,794
  • 16
  • 117
  • 146
Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
  • 1
    @chrisz, it's using `in` so if that was the case it'd be a syntax error (`argument of type 'bool' is not iterable`) – Holloway Feb 09 '18 at 23:30
  • Whoops, you are correct, I misread. – user3483203 Feb 09 '18 at 23:32
  • Evaluation order : http://www.informit.com/articles/article.aspx?p=459269&seqNum=11 – JacobIRR Feb 09 '18 at 23:33
  • Where exactly did you stumble upon this code? – JacobIRR Feb 09 '18 at 23:34
  • 1
    A previously deleted question on SO today. Their code was wrong because of that behaviour by the way, but they deleted the question before I could point it out. – Olivier Melançon Feb 09 '18 at 23:34
  • 4
    The expression is equivalent to `(1 in range(2)) and (range(2) == True)` of which the second part is false, making the whole condition false. – poke Feb 09 '18 at 23:34
  • I was mislead to think this had a special meaning, but now that you mention it, that makes sense. I admitedly did not realize the in operator could be chained. Thanks. – Olivier Melançon Feb 09 '18 at 23:43
  • 1
    @Olivier yeah, it's a little bit counter-intuitive that `in` is a comparison operator. I had to check the actual docs to confirm that suspicion, though. – juanpa.arrivillaga Feb 09 '18 at 23:50
  • Well it makes sense though. It is a binary operator that returns a boolean. I think it would be a design flaw not to treat it as such, especially since you can give it arbitrary behaviour with `__contains__` – Olivier Melançon Feb 09 '18 at 23:58

1 Answers1

14

This is due to the fact that both operators are comparison operators, so it is being interpreted as operator chaining:

https://docs.python.org/3.6/reference/expressions.html#comparisons

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).

So it is equivalent to:

>>> (1 in range(2)) and (range(2) == True)
False
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172