Python’s six pre-declared constants behave inconsistently, technical analysis finds
A new examination of Python’s core language features reveals that its six pre-declared constants are implemented in fundamentally different ways, ranging from lexical keywords to standard built-ins that can be easily overridden.
A technical analysis published on Hacker News has highlighted significant inconsistencies in how Python handles its six pre-declared constants: True, False, None, __debug__, Ellipsis, and NotImplemented. The review suggests that these fundamental elements of the language do not share a uniform architectural role, leading to unexpected behaviours for developers who rely on standard name resolution.
The analysis notes that True, False, and None function as lexical keywords rather than standard identifiers. Because they are resolved during the lexing phase rather than through regular name resolution, accessing them as attributes—for example, in an expression like x.True—results in a SyntaxError. This distinguishes them from other elements in the language, which are typically resolved as standard identifiers.
In contrast, __debug__ operates as a unique identifier that cannot be assigned to, even when used as an attribute. While it is syntactically valid to reference x.__debug__, doing so raises an AttributeError because the attribute does not exist, rather than a SyntaxError. The constant functions as a boolean that is normally True but becomes False when the interpreter is run with the -O flag, a mechanism similar to how assert statements are disabled in optimised builds.
The analysis further identifies Ellipsis and NotImplemented as standard built-ins rather than "real" constants. Unlike the other four constants, these two can be shadowed by global variables. This flexibility contrasts with the rigid definition of True, False, and None, which, despite being lexical tokens, also exist as normal built-ins accessible via the getattr function.
A particularly complex aspect of the implementation is that while setattr can modify the built-in values of True, False, and None, these changes do not affect the values accessed via their lexical tokens. Similarly, although __debug__ can technically be assigned to via the built-ins module, its value remains unaffected, reinforcing its status as a true constant despite not being a lexical token.
The post questions the historical rationale behind these differing treatments, noting that the specific reasons for classifying some constants as lexical tokens while leaving others as standard built-ins are not definitively stated. The analysis also points out that assigning to __debug__ is a rare instance where a SyntaxError is raised despite the syntax being technically valid, a quirk that underscores the inconsistent nature of the language’s design.


