Python 3.8 (or CPython 3.8?) added the warning
SyntaxWarning: "is" with a literal. Did you mean "=="?
for the code 0 is 0
.
I understand the warning, and I know the difference between is
and ==
.
However, I also know that CPython caches the object for small integers and shares it in other cases as well.
(Out of curiosity, I just checked the code (header) again.
Small ints are cached in tstate->interp->small_ints
.
0
and 1
are even more special and are stored globally in _PyLong_Zero
and _PyLong_One
.
All new creations of int
s are via PyLong_FromLong
and that one first checks if it is a small integer and cached.)
Given this background, if you know you have an int
object, you could say that the check x is 0
should be safe, right? Also, you could derive that 0 is 0
should always be True
, right? Or is this an implementation detail of CPython and other interpreters do not follow this? Which interpreter does not follow this?
Despite this more generic question (which I'm just curious about), consider this more specific (example) code:
def sum1a(*args):
y = 0
for x in args:
if y is 0:
y = x
else:
y = y + x
return y
Vs:
def sum1b(*args):
y = 0
for x in args:
if y == 0:
y = x
else:
y = y + x
return y
Vs:
def sum1c(*args):
y = None
for x in args:
if y is None:
y = x
else:
y = y + x
if y is None:
return 0
return y
Vs:
def sum2(*args):
y = 0
for x in args:
y = y + x
return y
The reason I would sometimes prefer sum1*
over sum2
is that depending on the library, sum1*
can really be more efficient. E.g. if the argument is a Numpy/TensorFlow/PyTorch array, you really would save a (potentially costly) operation here.
The reason I would prefer sum1a
over sum1b
is that sum1b
would break on certain inputs. E.g. if the input is a Numpy array, this would not work.
Of course, you could use sum1c
instead of sum1a
. However, sum1a
is shorter. So this is nicer?
If the answer to the original question is that this should always work, and if you agree that sum1a
is the best option then, how would you get rid of the warning? Is there a simple workaround? In general, I can see that the warning can be useful. So I would not want to disable it completely. I just want to disable it for this specific statement.
Maybe I could wrap it up in a function:
def is_(a, b):
return a is b
And then just use if is_(y, 0): ...
. Does this work? Is that a good idea?