builtins.True and friends are a fossil of pre-3.0 history. True and False started out as ordinary, reassignable builtins under PEP 285, landing first in 2.2.1 as a bugfix-release backport and then formalized properly in 2.3. None got its reassignment blocked in 2.4.True and False stayed assignable through the rest of the 2.x line and only became real keywords in 3.0.
The builtins slot is just what's left of that old namespace, kept around mostly so getattr/dir/vars introspection still works on them. It also explains why setattr(builtins, 'True', 67) doesn't touch the literal: the keyword compiles to a constant baked into the bytecode at compile time, separate from whatever's sitting in the builtins namespace.
As I understand it, True = False was a perfectly reasonable thing to do in Python 2.x, which means that the interpreter had to actually look up the True and False objects each time you referred to them!
an interesting side effect of this is that expressions like x.True raise a SyntaxError. i'm curious as to what the rationale was for this decision (if there was one).
A lot of things in Python can be explained by just how the language "design" works. The interpreter at one point did not have bools, they were just aliases to 1 and 0 which is why they are still integers today. However at one point someone on the way to Python 3 benchmarked the thing and realized that issuing LOAD_GLOBAL for these common constants is wasteful and they were reserved and compiled into bytecode instructions.
Ellipsis gained a whole token in Python 3 which is .... Previously that was only possible in certain subscript positions. So it's similar, just not by its name. NotImplemented is rare enough that there is really no point in making it a keyword. It's just a marker constant for comparisons.
I quibble: that doesn’t require that booleans be integers; a suitable __add__ method will do. And you probably want a def __int__(self): return 1 if self is True else 0 so that int(True) works. And before long you’ll have defined all other numerical methods, since this isn’t JavaScript where (true + true) ** (true + true + true) == 8 without booleans being integers because it’s eager to convert types. And… once you’ve defined __add__, __mul__, __div__, &c… well, duck typing doctrine will say it’s an integer after all. Ah well.
Besides all the observable behaviour, like addition being defined or 1 / False raising ZeroDivisionError`. You can look at the code to see that boolean's are defined as 0 and 1 here:
hongminhee | 10 hours ago
builtins.Trueand friends are a fossil of pre-3.0 history.TrueandFalsestarted out as ordinary, reassignable builtins under PEP 285, landing first in 2.2.1 as a bugfix-release backport and then formalized properly in 2.3.Nonegot its reassignment blocked in 2.4.TrueandFalsestayed assignable through the rest of the 2.x line and only became real keywords in 3.0.The builtins slot is just what's left of that old namespace, kept around mostly so
getattr/dir/varsintrospection still works on them. It also explains whysetattr(builtins, 'True', 67)doesn't touch the literal: the keyword compiles to a constant baked into the bytecode at compile time, separate from whatever's sitting in the builtins namespace.andyfriesen | an hour ago
As I understand it,
True = Falsewas a perfectly reasonable thing to do in Python 2.x, which means that the interpreter had to actually look up theTrueandFalseobjects each time you referred to them!chrismorgan | 50 minutes ago
It was never reasonable. Possible perhaps, but not reasonable.
tclancy | 32 minutes ago
A perfectly reasonable thing to do as a prank, yeah.
mitsuhiko | 9 hours ago
A lot of things in Python can be explained by just how the language "design" works. The interpreter at one point did not have bools, they were just aliases to 1 and 0 which is why they are still integers today. However at one point someone on the way to Python 3 benchmarked the thing and realized that issuing
LOAD_GLOBALfor these common constants is wasteful and they were reserved and compiled into bytecode instructions.Ellipsisgained a whole token in Python 3 which is.... Previously that was only possible in certain subscript positions. So it's similar, just not by its name.NotImplementedis rare enough that there is really no point in making it a keyword. It's just a marker constant for comparisons.chrismorgan | 8 hours ago
I don’t understand what you mean, since
getattr(builtins, "True") is True and type(True) is bool and bool is not int.pyfisch | 6 hours ago
This means you can do math with bools, like you would with integers:
chrismorgan | 51 minutes ago
I quibble: that doesn’t require that booleans be integers; a suitable
__add__method will do. And you probably want adef __int__(self): return 1 if self is True else 0so thatint(True)works. And before long you’ll have defined all other numerical methods, since this isn’t JavaScript where(true + true) ** (true + true + true) == 8without booleans being integers because it’s eager to convert types. And… once you’ve defined__add__,__mul__,__div__, &c… well, duck typing doctrine will say it’s an integer after all. Ah well.ojii | 8 hours ago
Try
isinstance(True, int)orTrue == 1chrismorgan | 8 hours ago
Oh yeah,
bool.__mro__ == (bool, int, object). Completely forgot about that.(
True == 1isn’t proof because of__eq__.)PuercoPop | 3 hours ago
Besides all the observable behaviour, like addition being defined or
1 / Falseraising ZeroDivisionError`. You can look at the code to see that boolean's are defined as 0 and 1 here:https://github.com/python/cpython/blob/9f8780022058e629dada82221c384b67c5687b2e/Objects/boolobject.c#L215-L226
You can also see that the the base type (tp_base) of PyBool_Type is defined as long (PyLong_Type)
erg | 3 hours ago
Slop language