
Christopher Barker writes:
But why are they keywords? In Py2, True and False were just identifiers- they were made keywords in py3.
I’m suggesting that one reason that they were made keywords is that it's useful for them to be singletons, which is much harder to do (or at least use consistently) if the names can be reassigned.
Except that the obvious assignments are True = 1 and False = 0, which both denote singletons (in CPython, anyway and as it happens).[1] That is, since bool() returns only True and False, as long as you use only those literals (0, False, 1, True) and bool(), "is" works as well as "==". So it's reserving the names, not the "singleton-ness", that's important. I assume that what the advocates of a true Boolean type wanted was {False, True} to be *disjoint* from the integers, so that "True is 1" is false. In order to give them actual literal names (ie, compiling to specific objects rather than to references to bindings), they needed to be made keywords. The whole Boolean project makes little sense unless the true and false objects have literal syntax, and in Python that means keyword names.
But these float special values can't be singletons anyway
Of course they *can*, they just aren't. There's no reason not to make 1e1000 is inf and (1e1000 - 1e999) is nan "work" (and it might even be more efficient to special-case them and return singletons rather than allocate objects and wrap them in floats -- if they were useful, but they're not so why bother?) But you don't want programmers doing something == nan or something == inf, either. For the former, they *have* to use math.isnan because of how math.nan.__eq__ is defined (and of course the system is probably delivering NaNs different from math.nan). The latter "works" *because* inf is a specific value (in IEEE 754), but it's nonsense interpreted as "infinity", since in Python it mostly denotes a value for an expression whose computation overflowed at some point but in many cases has a value in the real number system that is representable as a float.
Which means there really isn't a reason to make them keywords at all.
There's the same reason as for True and False: the principle that all values of certain fundamental types should have fixed literal representations in the language. The fact that there are other objects that are equal to inf would be the same as bool: True is not 1 but True == 1.
we would not want to [add new values of these types] even if it were even possible.
If you're referring to inf and nan, that's not clear. We could have inf_unrepresentable and inf_denormalized_or_unrepresentable (the latter would be used if you want any loss of precision due to "near" overflow to be considered inf), and nan_quiet and nan_signaling, so that operations that in math result in a NaN will all either have a NaN value which propagates or result in an immediate exception, depending on whether nan is the former or the latter.[2] I guess you could look at NumPy and friends for evidence whether those distinctions are ever worth making in Python programs, but at least in theory they're possibly useful. As for Booleans, Goedel showed that a consistent logic sufficient to support proving theorems of arithmetic is inherently 3-valued: A is True, A is False, A is Undecidable. But Goedel's logic is unimplementable in our computers by the very definition of undecidable. Some of the most subtle Buddhist texts are well-rationalized by a logic with four truth values for A: A is True, A is False, A is neither True nor False, A is both True and False. Some Buddhists are very tolerant, they accept inconsistent logics. Of course, we probably don't want to use those logics in computers. :-) Footnotes: [1] I'm not saying that the integer 1 is a singleton; it's not, there can be many distinct int objects equal to 1. I'm saying that the int denoted by "1" in a Python program is a singleton. [2] Of course these distinctions can't be made in plain Python, but they can be made from C extensions.