lichtung

x is None, not x == None (and NaN isn't equal to itself)

· #footguns #python

Two small Python habits head off a surprising number of bugs.

Use is for the singletons. is tests object identity — the same object in memory. == tests value, by calling __eq__, which any type is free to redefine. For None, True, and False, each a unique singleton, you want identity:

x is None        x is not None        x is False

Never write x == None. With a plain value it happens to work, but == dispatches through __eq__, and that can do anything. A NumPy array compared to None does an elementwise comparison and then raises when the result is used in a boolean context; pandas and SQLAlchemy have their own __eq__ quirks. x is None is the one form that always means exactly what it looks like — which is why every linter flags == None.

Use == for actual values: n == 0, s == "foo", arr == other. The whole rule is: identity for the singletons, equality for everything else.

NaN is not equal to itself. By IEEE 754 a NaN compares unequal to everything, itself included:

>>> float("nan") == float("nan")
False

So any NaN check written with == silently fails: x == float("nan") is always False, even when x is NaN. To detect NaN, use

import math
math.isnan(x)

or the shorthand x != x, which is True exactly when x is NaN.

And don't try to bridge the two ideas with x is float("nan"). float("nan") builds a fresh object on every call, so the identity check fails for a different reason than the equality one — two NaNs are neither == nor is. Only math.isnan (or x != x) actually works.