58

Today, in an interview, the CTO asked me what looks like an easy question,

What does this statement return ? :

None is None is None

I thought Python executed the first operation None is None and would return True. After that, it would compare True is None which would return False. But, to my surprise, the right answer is True. I am trying to find answer to this question, but after a couple of days searching I didn't find anything. Can someone explain why this happens?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Vladyslav
  • 2,018
  • 4
  • 18
  • 44
  • 12
    Python comparison operators chain. `None is None is None` is equivalent to `None is None and None is None`. – khelwood Jun 20 '18 at 14:52
  • 2
    to get the behaviour you expect, you would have to use parentheses: `if (None is None) is None`. – Zinki Jun 20 '18 at 14:53
  • 1
    interesting though. I never knew this could happen and I would have expected the same thing knowing what the `is` operator does... – L_Church Jun 20 '18 at 14:55
  • 3
    @SilvioMayolo Is this really a duplicate? It is not the same question. The answers are the same, but the question isn't. – MegaIng Jun 20 '18 at 15:04
  • 1
    [The](https://stackoverflow.com/questions/9284350/why-does-1-in-1-0-true-evaluate-to-false) [precedent](https://stackoverflow.com/questions/31354429/why-is-true-is-false-false-false-in-python/31354499) for these sorts of questions seems to be to use that particular one as a dup target. – Silvio Mayolo Jun 20 '18 at 15:17
  • 1
    Basically, I think that the _questioning process_ (the causes that brought him to ask this question) is significantly different from the other questions. This should be considered when flagging for duplicates, because it's the basis of which brings people here. – scharette Jun 20 '18 at 16:01
  • 1
    @scharette: The difference in the questioning process seems to amount to "I found this in the Queue.py source" vs "The CTO I was interviewing with asked me this". It seems entirely appropriate to dupe-close this question. – user2357112 Jun 20 '18 at 16:43

3 Answers3

40

The bytecode shows that two comparisons are being performed here with the middle being duplicated:

>>> import dis
>>> def a():
...     return None is None is None
... 
>>> dis.dis(a)
  2           0 LOAD_CONST               0 (None)
              3 LOAD_CONST               0 (None)
              6 DUP_TOP
              7 ROT_THREE
              8 COMPARE_OP               8 (is)
             11 JUMP_IF_FALSE_OR_POP    21
             14 LOAD_CONST               0 (None)
             17 COMPARE_OP               8 (is)
             20 RETURN_VALUE
        >>   21 ROT_TWO
             22 POP_TOP
             23 RETURN_VALUE

As stated in the docs for comparisons this is because these operators chain together.

a op b op c will be translated to a op b and b op c (note b is duplicated in the bytecode as shown above)

shuttle87
  • 15,466
  • 11
  • 77
  • 106
33

As some people comments, Python comparisons can be chained.

For the sake of explanation, when chaining, Python actually ANDs the expressions.

The rationale behind this, is that expressions like a < b < c have the interpretation that is conventional in mathematics. Hence the confusion of your particular expression None is None is None where identy operators are involved.

So basically, this would translate to:

(None is None) and (None is None)

which is clearly True

Here is another example in the Python docs

Further Information

Especially since this was an interview question, it is important to note that this is not a general behavior shared among all languages.

As it is stated in the documentation I linked,

Unlike C, all comparison operations in Python have the same priority, which is lower than that of any arithmetic, shifting or bitwise operation.

So, let's consider the 10 > x > 2 expression (since is operator is not valid in C).

C's translation (because of operator precedence)

((10 > x) > 2)

Python's translation

(10 > x) and (x > 2)
scharette
  • 9,437
  • 8
  • 33
  • 67
  • 5
    I like this answer because it explains the rationale behind the behavior: ***expressions like a < b < c have the interpretation that is conventional in mathematics***. Seeing how it is implemented in bytecode, in my opinion, explains nothing that would allow me to be able to extrapolate to other cases. – Jim Jun 20 '18 at 15:52
  • @Jim Your comment is really appreciated. For future users I made sure to put more emphasis on that point because it is indeed the true reason behind this behavior. `None is None is None` is a particular misleading statement following this logic since no mathematics is involved.. – scharette Jun 20 '18 at 16:20
  • Although it's true there's no *mathematics* involved, the result still matches colloquial usage. For instance, "a fool is a fool is a fool" might be used for emphasis, while "an idiot is a fool is a nitwit" would imply equivalence between all three terms. – IMSoP Jun 20 '18 at 16:29
  • @IMSoP You're right, _mathematics_ wasn't the good term to use. See my answer I actually meant that _identy_ operators are used which is really confusing compared to the same behavior when using _comparison_ operators like `<` if that make sense. – scharette Jun 20 '18 at 16:32
  • I'm not sure it's any more or less surprising with identity operators; they're just another type of comparison. As I say, we'd use "is" that way in English, and a mathematician might well write `x = y = z`, or `x ≡ y ≡ z`, etc. I think it's just surprising because programming languages don't normally have operators that behave this way. – IMSoP Jun 20 '18 at 17:15
10

is is a comparison operator, as seen in the docs:

comparison    ::=  or_expr ( comp_operator or_expr )*
comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"

So just like the other comparison operators, it can be chained arbitrarily. So

a = b = c = None
a is b is c

is equivalent to

(a is b) and (b is c)
miradulo
  • 28,857
  • 6
  • 80
  • 93
  • 1
    Note that assignment is not a comparison with the same semantics. Specifically `a = b = c` is not evaluated in the same was as `a is b is c` or similar. `a = b = c` works from right to left. Whereas `a comp b comp c` is transformed into `a comp b` and `b comp c`. `a = b = c` is transformed into `b = c; a = b` – shuttle87 Jun 21 '18 at 06:49
  • 1
    @shuttle87 Yes.. I simply wrote `a = b = c = None` for the subsequent example. – miradulo Jun 22 '18 at 13:57