Search Agent Sky
← Back to search Memory Lane Recent answers
Cited source trail
Python's pre-declared constants are kinda weird
Original question
Investigate this live topic: Python's pre-declared constants are kinda weird. Start with https://sebsite.pw/w/20260801-pythonconstants.html and browse beyond it. Summarize what changed, why it matters, and cite the strongest sources.
Sources checkedsebsite.pwdocs.python.orgdiscuss.python.orgpeps.python.orgnews.linxi.com.aulobste.rs
Next step

Research any question with live sources, then publish the cited answer as a free shareable link.

## Python's pre-declared constants are "kinda weird" — what changed and why it matters ### What the article claims The post by "seb" at [sebsite.pw](https://sebsite.pw/w/20260801-pythonconstants.html) (2026-08-01) observes that Python's six documented "built-in constants" — `True`, `False`, `None`, `__debug__`, `Ellipsis`/`...`, and `NotImplemented` — are implemented in fundamentally different ways, despite all being grouped together in the docs: - **`True`, `False`, `None`** are hard **keywords** (lexical tokens), not identifiers. So `x.True` raises a `SyntaxError`, and they can't be shadowed. - **`__debug__`** is a normal identifier but is the *only* identifier in the language that cannot be assigned to (even as an attribute: `x.__debug__ = 67` → `SyntaxError: cannot assign to __debug__`). It's `True` normally, `False` under `-O`. - **`Ellipsis` and `NotImplemented`** are *not* real constants at all — they're ordinary builtins that can be shadowed (`NotImplemented = 67` works). Notably, `...` is a real constant (assignment to `...` is a `SyntaxError`) while `Ellipsis` is reassignable. - A quirk: `setattr(builtins, 'True', 67)` changes the builtins slot but **not** the value seen via the keyword — the keyword compiles to a constant baked into bytecode at compile time, independent of the builtins namespace. `__debug__` behaves the same way. - A "SyntaxError is a lie" tangent: `assert (__debug__ := 67)` raises a `SyntaxError` in debug builds but compiles fine under `-O` (since the assert is never compiled) — one of only a few cases where a `SyntaxError` is raised for something that isn't actually invalid syntax. ### Why it matters / the rationale The official docs confirm the split: the [Python 3.14.7 Built-in Constants page](https://docs.python.org/3/library/constants.html) states that `None`, `False`, `True`, and `__debug__` "cannot be reassigned (assignments to them, even as an attribute name, raise SyntaxError), so they can be considered 'true' constants" — while explicitly noting "Assignment to `Ellipsis` is possible, but assignment to `...` raises a SyntaxError." The historical rationale is well documented: - **`True`/`False`/`None` became keywords in Python 3** as part of [PEP 3100](https://peps.python.org/pep-3100/), which lists "None becomes a keyword; also True and False" under Core language changes. The [Lobsters discussion](https://lobste.rs/s/62kxco/python_s_pre_declared_constants_are_kinda) adds detail: `True`/`False` started as ordinary reassignable builtins under PEP 285 (2.2.1/2.3), `None` got reassignment blocked in 2.4, and they only became true keywords in 3.0. The builtins slots are "a fossil of pre-3.0 history," kept so `getattr`/`dir`/`vars` introspection still works. - **Why keywords get special treatment:** core dev Alyssa Coghlan (ncoghlan) explains on [discuss.python.org](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073) that the compiler uses these singletons for control-flow optimizations (e.g., optimizing out "always true/false" checks), so shadowing them would break those optimizations. Making them hard keywords was the Python 3 fix. - **Why `Ellipsis`/`NotImplemented` are left assignable:** they're "not constants, but builtin singletons" (per Cornelius Krupp in the same thread). Python is a dynamic language where you can break almost anything (`int`, `type`, `__import__`, etc.), and the "consenting adults" design principle accepts that trade-off. `NotImplemented` is "rare enough that there is really no point in making it a keyword" (mitsuhiko on Lobsters). ### Practical impact - **`NotImplemented` is genuinely dangerous to reassign** — a professor (Craig Partridge) demonstrated on [discuss.python.org](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073) that overwriting it makes binary special methods like `__add__()` return actual values instead of `NotImplemented`, silently breaking operator fallback logic. The safe practice is to always use `...` rather than `Ellipsis`. - **`__debug__` is a true special case** — the only non-keyword identifier you can't assign to, and the only case where a `SyntaxError` is raised for syntactically valid code (the `assert (__debug__ := 67)` trick). There's even an active [discuss.python.org proposal](https://discuss.python.org/t/allow-debug-to-be-set-at-runtime/64840) to allow setting `__debug__` at runtime, which currently fails with `SyntaxError: cannot assign to __debug__`. ### Strongest sources - [sebsite.pw — python's pre-declared constants are kinda weird](https://sebsite.pw/w/20260801-pythonconstants.html) (the original analysis) - [Python 3.14.7 — Built-in Constants (official docs)](https://docs.python.org/3/library/constants.html) (authoritative statement of the behavior) - [discuss.python.org — Assignment to Ellipsis and NotImplemented](https://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073) (core-dev rationale: compiler optimizations + "consenting adults") - [PEP 3100 — Miscellaneous Python 3.0 Plans](https://peps.python.org/pep-3100/) (the change that made None/True/False keywords) - [Lobsters discussion](https://lobste.rs/s/62kxco/python_s_pre_declared_constants_are_kinda) (historical context: PEP 285, pre-3.0 builtins, bytecode compilation) - [Linxi News summary](https://news.linxi.com.au/news/pythons-six-pre-declared-constants-behave-inconsistently-technical-analysis-finds) (secondary coverage of the Hacker News discussion) **Bottom line:** The "weirdness" is a historical artifact. `True`/`False`/`None`/`__debug__` are protected because the compiler relies on them for optimizations; `Ellipsis` and `NotImplemented` were left as ordinary (shadowable) builtins because they're rare and Python's design philosophy doesn't protect against every mistake. The inconsistency is intentional, not a bug.

Sources visited

1navigatehttps://sebsite.pw/w/20260801-pythonconstants.html
2searchPython pre-declared constants True False None __debug__ Ellipsis NotImplemented weird
3searchPython __debug__ cannot assign SyntaxError constant builtins
4navigatehttps://docs.python.org/3/library/constants.html
5navigatehttps://stackoverflow.com/questions/44971482/why-are-2-of-the-6-built-in-constants-assignable
6navigatehttps://discuss.python.org/t/assignment-to-ellipsis-and-notimplemented/78073
7navigatehttps://peps.python.org/pep-3100/
8navigatehttps://news.linxi.com.au/news/pythons-six-pre-declared-constants-behave-inconsistently-technical-analysis-finds
9searchHacker News python pre-declared constants weird sebsite
10navigatehttps://news.ycombinator.com/item?id=46143748
11searchnews.ycombinator.com python's pre-declared constants are kinda weird
12navigatehttps://lobste.rs/s/62kxco/python_s_pre_declared_constants_are_kinda
Shared by Proto Státis · Aug 25, 2026

Ask your own question

Search live sources for a question you choose. You can publish the cited answer as a free link.