Python's pre-declared constants are kinda weird

Aug 26, 2026 04:39 AM - 1 hour ago 1

python has 6 pre-declared "constants": True, False, None, __debug__, Ellipsis (or equivalently ...), and NotImplemented. but they each behave somewhat differently, for immoderate reason.

True, False, and None

True, False, and None are keywords. they aren't identifiers, they're conscionable consecutive up their ain lexical tokens. which is really weird; thing other is for illustration this successful python. usually worldly is resolved during regular sanction resolution, not successful the lexer itself.

an absorbing broadside effect of this is that expressions for illustration x.True raise a SyntaxError. i'm funny arsenic to what the rationale was for this determination (if location was one).

there's immoderate much absorbing worldly pinch these constants, but i'll get to it later, since it ties successful pinch the different constants.

__debug__

__debug__ is simply a boolean constant: it's usually True, but erstwhile moving pinch -O, it's False. the thought is akin to really asseverate is abnormal successful non-debug builds: you tin wrap codification successful if __debug__ if the cheque would beryllium excessively costly successful an "optimized" build, aliases something.

__debug__ is really absorbing though, because though it's a normal identifier (unlike True, False, and None), it's the only identifier successful the connection which can't beryllium assigned to:

>>> __debug__ = 67 File "", statement 1 SyntaxError: cannot delegate to __debug__

you can't moreover delegate to it arsenic an attribute:

>>> x.__debug__ = 67 File "", statement 1 SyntaxError: cannot delegate to __debug__

again, nary different identifier behaves for illustration this. this is simply a existent typical case.

but because it's not a keyword, it behaves somewhat otherwise to True, False, and None:
x.__debug__ raises AttributeError (rather than SyntaxError), since it's syntactically valid; it's conscionable looking up an property which doesn't exist.

interestingly, there's besides a typical correction connection for attempting to delete __debug__ (despite the truth that this would raise a NameError anyhow if not for the typical case), but this doesn't use for deleting an property named __debug__:

>>> del __debug__ File "", statement 1 SyntaxError: cannot delete __debug__ >>> del x.__debug__ Traceback (most caller telephone last): File "", statement 1, successful NameError: sanction 'x' is not defined

if x were defined, an AttributeError would beryllium raised instead. successful either case, it's not a SyntaxError (unlike assignment), for immoderate reason.

tangent: SyntaxError is simply a lie

speaking of errors: assigning to __debug__ is 1 of only a fewer cases i'm alert of wherever a SyntaxError is raised contempt thing not really being invalid syntax. here, you tin corroborate it yourself:

>>> asseverate (__debug__ := 67)

running that asseverate successful a debug build raises a SyntaxError, but pinch -O, the assertion is ne'er compiled, and truthful nary objection is raised.

two different instances of this are utilizing output aliases await extracurricular of a function:

>>> asseverate (yield) >>> asseverate (await 67)

Ellipsis and NotImplemented

Ellipsis and NotImplemented are documented successful the "constants" conception of the reference, but dissimilar the different 4 constants, they aren't "real" constants. they're conscionable normal builtins, truthful they tin beryllium shadowed by globals:

>>> NotImplemented = 67 >>> NotImplemented 67

again, i'm funny astir the rationale here. why is it that these aren't special, but the different constants are?

overwriting constants

here's thing interesting: contempt being lexical tokens, True, False, and None besides beryllium arsenic normal builtins:

>>> import builtins >>> getattr(builtins, 'True') True >>> getattr(builtins, 'False') False >>> getattr(builtins, 'None') is None True

there's nary measurement to straight entree these without utilizing getattr.

but here's wherever things get really interesting: setattr besides works!

>>> setattr(builtins, 'True', 67) >>> getattr(builtins, 'True') 67

however, this doesn't alteration the worth erstwhile accessed pinch the lexical token:

>>> True True

but __debug__ has the aforesaid behavior!

>>> setattr(builtins, '__debug__', 67) >>> builtins.__debug__ 67 >>> __debug__ True

so __debug__ tin sorta beryllium assigned to, but contempt not being a lexical token, it's typical cased conscionable for illustration True, False, and None: its worth is unaffected by changes to the builtins module. truthful it really is simply a constant!

Ellipsis and NotImplemented are, erstwhile again, not really constants:

>>> setattr(builtins, 'Ellipsis', 67) >>> Ellipsis 67

this doesn't alteration the worth of ... though:

>>> ... Ellipsis

so successful immoderate sense, ... is simply a existent constant, but Ellipsis isn't. weird, right?

More