On the basis of what, sorry? None
is defined in not only the docs but in the language specification as being a singleton built-in constant:
This type has a single value. There is a single object with this value. This object is accessed through the built-in name None
.
I’m curious what the rationale would be for changing this now.
Given that per the language definition, None
is a singleton built-in constant, identity rather than equality checking s semantically the most appropriate operation to use. Additionally, there is a performance advantage, with equality checking being substantially slower.
For example, in Python 3.11, comparing even trivial instances of the built-in types (str
, int
, object()
, etc) to None
is nearly 50% slower for equality vs. identity:
$ python -m timeit --setup "obj = 'test'" -- "obj is None"
5000000 loops, best of 5: 43.3 nsec per loop
$ python -m timeit --setup "obj = 'test'" -- "obj == None"
5000000 loops, best of 5: 64.8 nsec per loop
While for a simple custom class:
class Spam():
def __init__(self, value):
self.value = value
def __eq__(self, other):
if self is other:
return True
if type(other) is not type(self):
return False
return other.value == self.value
It is around 7 times slower:
$ python -m timeit --setup "from temp import Spam; obj = Spam(42)" -- "obj is None"
5000000 loops, best of 5: 43.3 nsec per loop
$ python -m timeit --setup "from temp import Spam; obj = Spam(42)" -- "obj == None"
1000000 loops, best of 5: 304 nsec per loop
Even for the best case, objects that are None
, there is still around a 40% speed penalty due to the extra lookup and logic:
$ python -m timeit --setup "obj = None" -- "obj is None"
5000000 loops, best of 5: 43.3 nsec per loop
$ python -m timeit --setup "obj = None" -- "obj == None"
5000000 loops, best of 5: 60.1 nsec per loop
On older versions of Python, e.g. 3.7, the speed penalty is even greater, e.g. nearly 15x for trivial classes with a custom __eq__
:
$ python -m timeit --setup "import temp; obj = temp.Spam(42)" -- "obj is None"
10000000 loops, best of 5: 36.9 nsec per loop
$ python -m timeit --setup "import temp; obj = temp.Spam(42)" -- "obj == None"
500000 loops, best of 5: 522 nsec per loop