Why does Python log a SyntaxWarning for 'is' with literals?

2020-01-21 Warning snake

Take this reasonable-looking code:

x = 200
if x is 200:
    print("It's 200!")

If we run it with Python 3.8+, we’ll see a warning message on line 2:

$ python ~/tmp/test.py
/Users/chainz/tmp/test.py:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
  if x is 200:
It's 200!

This is a new warning added in Python 3.8. From the release notes:

The compiler now produces a SyntaxWarning when identity checks (is and is not) are used with certain types of literals (e.g. strings, numbers). These can often work by accident in CPython, but are not guaranteed by the language spec. The warning advises users to use equality tests (== and !=) instead. (Contributed by Serhiy Storchaka in bpo-34850.)

What does it mean that they “work by accident”? To understand that, let’s take a look at a broken example.

Take this new code:

x = int(1000.0)
if x is 1000:
    print("It's 1000!")

It’s similar to the previous example. We’re again assigning x to an int value, but this time construct it via the float literal 1000.0.

Apart from this time, our message does not appear, only the warning:

$ python ~/tmp/test.py
/Users/chainz/tmp/test.py:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
  if x is 1000:

If we follow the warning and change is to ==, the message will appear with no SyntaxWarning:

$ python ~/tmp/test.py
It's 1000!

Why didn’t it appear before? It’s all to do with the difference between is and == (the same for is not and !=).

is checks for identity - if the two variables point to the exact same object.

== checks for equality - if the two variables point at values are equal. That is, if they will act the same way in the same situations.

Identity implies equality, but not the other way round.

CPython (the main implementation of Python) can reuse some objects to improve performance. For example, when it starts up, it pre-creates int objects for the numbers -5 to 256. Other Python implementations such as MicroPython don’t necessarily do that.

In our first example, when we assigned x to 200 on line 1, and compared it to another definition of 200 on line 2, CPython would reuse the same int object for both. Thus the is operator on line 2 returned True.

In our second example, the 1000 on line 2 was created at import time, when Python parsed the code. The 1000 value in x on line 1 was only constructed at runtime. Since it’s outside the range of pre-created int objects, that would be a newly created int. Thus the is operator returned False.

The SyntaxWarning check applies to other types with literals too - for example bytes, float, and str.

Other Reading

Identity versus equality is a core programming concept that’s worth nailing. Here’s some other reading with explanations that might beat mine!

Fin

Hope this helps you understand and fix this warning,

—Adam


Working on a Django project? Check out my book Speed Up Your Django Tests which covers loads of best practices so you can write faster, more accurate tests.


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: python