Python's pre-declared constants reveal deep implementation inconsistencies
A technical analysis shows that Python's fundamental constants operate under disparate rules, ranging from lexical tokens to standard built-ins.
Python's core constants—True, False, None, __debug__, Ellipsis, and NotImplemented—appear uniform to the average developer, but they are governed by wildly different internal rules. A technical analysis reveals that these six constants are implemented using disparate mechanisms, leading to inconsistent behaviors regarding assignment, shadowing, and attribute access.
The research highlights a three-tiered hierarchy of constants. True, False, and None are treated as lexical tokens, or keywords, by the Python lexer. Because they are tokens rather than standard identifiers, attempts to use them as attributes—such as writing 'x.True'—result in a SyntaxError. In contrast, Ellipsis and NotImplemented are standard built-in objects. Unlike the lexical tokens, these two can be shadowed by global variables within a local scope.
The unique case of __debug__
Standing apart from both the keywords and the standard built-ins is __debug__. This is the only identifier in the language that cannot be assigned to or deleted. Any attempt to modify its value or remove it from the namespace triggers a SyntaxError, making it a unique entity in Python's identifier system.
Built-in duality
Further complexity exists in how Python handles the relationship between tokens and the builtins module. While True, False, and None are keywords, they also exist as objects within the builtins module. Developers can access them using `getattr(builtins, 'True')` and can even modify the builtins module using `setattr`. However, modifying the builtins module does not change the value or behavior of the lexical tokens themselves, creating a duality where the keyword and the built-in object coexist separately.
Why the inconsistency matters
These discrepancies are more than academic curiosities; they are critical for developers engaged in metaprogramming, compiler design, or deep system debugging. The lack of uniformity is a byproduct of Python's evolutionary history, where different constants were introduced at various stages of development using different implementation strategies. Understanding these nuances allows developers to avoid subtle bugs when manipulating the language's internal state.
What remains to be explored
While the core behaviors of these constants are now mapped, the analysis suggests that the boundaries of these rules can be complex. Developers should continue to monitor how these constants interact with newer language features, as the intersection of lexical tokens and dynamic built-ins remains one of the more idiosyncratic corners of the Python runtime.