Explaining Python Builtin Constants
Python# Correct singleton comparisons: use `is`, not `==` if value is None: ... # NotImplemented: return from special methods when operation unsupported class Vector: def __eq__(self, other): if not isinstance(other, Vector): return NotImplemented return self.x == other.x and self.y == other.y # Ellipsis: placeholder in stubs/slices def stub_function() -> int: ... matrix[..., 0] # __debug__: guards code stripped in optimized mode (-O) if __debug__: assert expensive_check(), "This block vanishes under python -O"
-
Identify which constant applies to the situation:
True/False— boolean values (instances ofintsubclassbool)None— absence of a value (the sole instance ofNoneType)NotImplemented— signal to Python that a binary operator/comparison isn't supported for these typesEllipsis(...) — placeholder (slices, stubs, unimplemented bodies)__debug__—Trueunless run with-O/-OO; controlsassertexecution
-
Use identity checks (
is/is not) for singletons — never==forNone,True,False,NotImplemented,Ellipsis. These are guaranteed singletons; identity comparison is faster and semantically correct. -
For operator overloading, return
NotImplemented(not raise an exception, not returnFalse) when your type can't handle the other operand. Python then tries the reflected method or raisesTypeErroritself. -
Never evaluate
NotImplementedin a boolean context directly — it's truthy and doingif x == y:where__eq__might returnNotImplementedcan silently produce wrong results if not handled by Python's dispatch. Only return it from special methods; don't manually branch on it in general code. -
For
__debug__-guarded code, remember it's a compile-time constant — theif __debug__:block is literally removed from bytecode under-O. Don't put code with side effects required for correctness inside it (same rule asassert).
Example 1:
Input: Reviewing code with if x == None:
Output: Flag and rewrite as if x is None: — == can be overridden by __eq__ and is semantically wrong for singleton identity checks; is is also faster.
Example 2:
Input: Implementing __lt__ for a custom class comparing against arbitrary types
Output:
Pythondef __lt__(self, other): if not isinstance(other, MyClass): return NotImplemented return self.value < other.value
Example 3:
Input: Writing a .pyi stub file for a function body
Output:
Pythondef connect(host: str, port: int) -> Connection: ...
... (Ellipsis) is the idiomatic stub body placeholder, distinct from pass.
Example 4:
Input: assert user.is_authenticated(), "must be logged in" used to enforce security
Output: Flag as a bug — under python -O, __debug__ is False and all assert statements are compiled out, so this check silently disappears. Use an explicit if not ...: raise PermissionError(...) instead.
- Treat
True/Falseasintsubtypes when relevant (True == 1,isinstance(True, int)isTrue) but don't rely on this for readability — prefer explicit booleans in conditionals. - Use
Noneas the default sentinel for "no value provided" in function signatures, but ifNoneis a valid meaningful argument, use a dedicated sentinel object instead. - Document custom classes'
__eq__/__lt__/etc. as returningNotImplementedfor unsupported types so subclasses and reflected operations behave correctly. - Use
Ellipsisin NumPy-style slicing (arr[..., 0]) and in type-stub/interface bodies; avoid using it as a generic "TODO" placeholder in production logic (useraise NotImplementedErrorinstead — different fromNotImplemented).
- Confusing
NotImplementedwithNotImplementedError.NotImplementedis a constant returned from special methods;NotImplementedErroris an exception raised in abstract method bodies. They are not interchangeable. - Using
==instead ofisforNone/singletons — fragile if__eq__is overridden. - Putting required side effects inside
if __debug__:orassert— vanishes under-O. - Returning
Falseinstead ofNotImplementedfrom comparison dunders when the type doesn't match — this incorrectly asserts inequality/falsity rather than deferring to the other operand's reflected method. - Using
Ellipsisas an empty function body placeholder in real (non-stub) code wherepassor a real implementation is expected — reserve...for stubs/interfaces.