python has 6 pre-declared “constants”:True,False,None,__debug__,Ellipsis(or equivalently…), andNotImplemented. but they all behave slightly differently, for some reason.True,False, andNoneTrue,False, andNoneare keywords. they aren’t identifiers, they’re just straight up their own lexical tokens. which is really weird; nothing else is like this in python. usually stuff is resolved during regular name resolution, not in the lexer itself.an interesting side effect of this is that expressions likex.Trueraise aSyntaxError. i’m curious as to what the rationale was for this decision (if there was one).there’s some more interesting stuff with these constants, but i’ll get to it later, since it ties in with the other constants.__debug____debug__is a boolean constant: it’s normallyTrue, but when running with-O, it’sFalse. the idea is similar to howassertis disabled in non-debug builds: you can wrap code inif __debug__if the check would be too expensive in an “optimized” build, or something.__debug__is really interesting though, because although it’s a normal identifier (unlikeTrue,False, andNone), it’s the only identifier in the language which can’t be assigned to:>>>__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__you can’t even assign to it as an attribute:>>>x.__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
True,False, andNoneare keywords. they aren’t identifiers, they’re just straight up their own lexical tokens. which is really weird; nothing else is like this in python. usually stuff is resolved during regular name resolution, not in the lexer itself.an interesting side effect of this is that expressions likex.Trueraise aSyntaxError. i’m curious as to what the rationale was for this decision (if there was one).there’s some more interesting stuff with these constants, but i’ll get to it later, since it ties in with the other constants.__debug____debug__is a boolean constant: it’s normallyTrue, but when running with-O, it’sFalse. the idea is similar to howassertis disabled in non-debug builds: you can wrap code inif __debug__if the check would be too expensive in an “optimized” build, or something.__debug__is really interesting though, because although it’s a normal identifier (unlikeTrue,False, andNone), it’s the only identifier in the language which can’t be assigned to:>>>__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__you can’t even assign to it as an attribute:>>>x.__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
an interesting side effect of this is that expressions likex.Trueraise aSyntaxError. i’m curious as to what the rationale was for this decision (if there was one).there’s some more interesting stuff with these constants, but i’ll get to it later, since it ties in with the other constants.__debug____debug__is a boolean constant: it’s normallyTrue, but when running with-O, it’sFalse. the idea is similar to howassertis disabled in non-debug builds: you can wrap code inif __debug__if the check would be too expensive in an “optimized” build, or something.__debug__is really interesting though, because although it’s a normal identifier (unlikeTrue,False, andNone), it’s the only identifier in the language which can’t be assigned to:>>>__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__you can’t even assign to it as an attribute:>>>x.__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
there’s some more interesting stuff with these constants, but i’ll get to it later, since it ties in with the other constants.__debug____debug__is a boolean constant: it’s normallyTrue, but when running with-O, it’sFalse. the idea is similar to howassertis disabled in non-debug builds: you can wrap code inif __debug__if the check would be too expensive in an “optimized” build, or something.__debug__is really interesting though, because although it’s a normal identifier (unlikeTrue,False, andNone), it’s the only identifier in the language which can’t be assigned to:>>>__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__you can’t even assign to it as an attribute:>>>x.__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
__debug__is a boolean constant: it’s normallyTrue, but when running with-O, it’sFalse. the idea is similar to howassertis disabled in non-debug builds: you can wrap code inif __debug__if the check would be too expensive in an “optimized” build, or something.__debug__is really interesting though, because although it’s a normal identifier (unlikeTrue,False, andNone), it’s the only identifier in the language which can’t be assigned to:>>>__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__you can’t even assign to it as an attribute:>>>x.__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
__debug__is really interesting though, because although it’s a normal identifier (unlikeTrue,False, andNone), it’s the only identifier in the language which can’t be assigned to:>>>__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__you can’t even assign to it as an attribute:>>>x.__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
you can’t even assign to it as an attribute:>>>x.__debug__ = 67File “”, line 1
SyntaxError: cannot assign to __debug__again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
again, no other identifier behaves like this. this is a true special case.but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
but because it’s not a keyword, it behaves slightly differently toTrue,False, andNone:x.__debug__raisesAttributeError(rather thanSyntaxError), since it’s syntactically valid; it’s just looking up an attribute which doesn’t exist.interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
interestingly, there’s also a special error message for attempting to delete__debug__(despite the fact that this would raise aNameErroranyway if not for the special case), but thisdoesn’tapply for deleting an attribute named__debug__:>>>del __debug__File “”, line 1
SyntaxError: cannot delete __debug__
>>>del x.__debug__Traceback (most recent call last):
File “”, line 1, inNameError: name ‘x’ is not definedifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
ifxwere defined, anAttributeErrorwould be raised instead. in either case, it’s not aSyntaxError(unlike assignment), for some reason.tangent:SyntaxErroris a liespeaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?
speaking of errors: assigning to__debug__is one of only a few cases i’m aware of where aSyntaxErroris raised despite something not actually being invalid syntax. here, you can confirm it yourself:>>>assert (__debug__ := 67)running that assert in a debug build raises aSyntaxError, but with-O, the assertion is never compiled, and so no exception is raised.two other instances of this are usingyieldorawaitoutside of a function:>>>assert (yield)>>>assert (await 67)EllipsisandNotImplementedEllipsisandNotImplementedare documented in the “constants” section of the reference, but unlike the other 4 constants, they aren’t “real” constants. they’re just normal builtins, so they can be shadowed by globals:>>>NotImplemented = 67>>>NotImplemented67again, i’m curious about the rationale here. why is it that these aren’t special, but the other constants are?overwriting constantshere’s something interesting: despite being lexical tokens,True,False, andNonealso exist as normal builtins:>>>import builtins>>>getattr(builtins, ‘True’)True
>>>getattr(builtins, ‘False’)False
>>>getattr(builtins, ‘None’) is NoneTruethere’s no way to directly access these without usinggetattr.but here’s where things get really interesting:setattralso works!>>>setattr(builtins, ‘True’, 67)>>>getattr(builtins, ‘True’)67however, this doesn’t change the value when accessed with the lexical token:>>>TrueTruebut__debug__has the same behavior!>>>setattr(builtins, ‘__debug__’, 67)>>>builtins.__debug__67
>>>__debug__Trueso__debug__cansortabe assigned to, but despite not being a lexical token, it’s special cased just likeTrue,False, andNone: its value is unaffected by changes to the builtins module. so it really is a constant!EllipsisandNotImplementedare, once again, not actually constants:>>>setattr(builtins, ‘Ellipsis’, 67)>>>Ellipsis67this doesn’t change the value of…though:>>>…Ellipsisso in some sense,…is a real constant, butEllipsisisn’t. weird, right?