Why does Python log a SyntaxWarning for ‘is’ with literals?
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
andis 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!
- Trey Hunner has a great article on sentinel values that covers Equality vs identity.
- Stack Overflow on What’s with the integer cache maintained by the interpreter?
- Flake8 has a similar check with its error code F632. If you’re looking to improve the general quality of your Python code, running Flake8 as part of your development cycle will help.
Read my book Boost Your Git DX to Git better.
One summary email a week, no spam, I pinky promise.
Related posts:
- Why does Python log a SyntaxWarning saying “object is not callable”?
- Why does Python log a SyntaxWarning saying “object is not subscriptable”?
- Why does Python log a SyntaxWarning saying “assertion is always true”?
- Why does Python log a SyntaxWarning saying “list indices must be integers or slices”?
Tags: python