Permalink
Cannot retrieve contributors at this time
457 lines (347 sloc)
16 KB
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| PEP: 285 | |
| Title: Adding a bool type | |
| Version: $Revision$ | |
| Last-Modified: $Date$ | |
| Author: guido@python.org (Guido van Rossum) | |
| Status: Final | |
| Type: Standards Track | |
| Content-Type: text/x-rst | |
| Created: 08-Mar-2002 | |
| Python-Version: 2.3 | |
| Post-History: 08-Mar-2002, 30-Mar-2002, 03-Apr-2002 | |
| Abstract | |
| ======== | |
| This PEP proposes the introduction of a new built-in type, bool, | |
| with two constants, ``False`` and ``True``. The bool type would be a | |
| straightforward subtype (in C) of the int type, and the values | |
| ``False`` and ``True`` would behave like 0 and 1 in most respects (for | |
| example, ``False==0`` and ``True==1`` would be true) except ``repr()`` and | |
| ``str()``. All built-in operations that conceptually return a Boolean | |
| result will be changed to return ``False`` or ``True`` instead of 0 or 1; | |
| for example, comparisons, the "not" operator, and predicates like | |
| ``isinstance()``. | |
| Review | |
| ====== | |
| I've collected enough feedback to last me a lifetime, so I declare | |
| the review period officially OVER. I had Chinese food today; my | |
| fortune cookie said "Strong and bitter words indicate a weak | |
| cause." It reminded me of some of the posts against this | |
| PEP... :-) | |
| Anyway, here are my BDFL pronouncements. (Executive summary: I'm | |
| not changing a thing; all variants are rejected.) | |
| 1) Should this PEP be accepted? | |
| => Yes. | |
| There have been many arguments against the PEP. Many of them | |
| were based on misunderstandings. I've tried to clarify some of | |
| the most common misunderstandings below in the main text of the | |
| PEP. The only issue that weighs at all for me is the tendency | |
| of newbies to write "if x == True" where "if x" would suffice. | |
| More about that below too. I think this is not a sufficient | |
| reason to reject the PEP. | |
| 2) Should ``str(True)`` return "True" or "1"? "1" might reduce | |
| backwards compatibility problems, but looks strange. | |
| (``repr(True)`` would always return "True".) | |
| => "True". | |
| Almost all reviewers agree with this. | |
| 3) Should the constants be called 'True' and 'False' (similar to | |
| None) or 'true' and 'false' (as in C++, Java and C99)? | |
| => True and False. | |
| Most reviewers agree that consistency within Python is more | |
| important than consistency with other languages. | |
| 4) Should we strive to eliminate non-Boolean operations on bools | |
| in the future, through suitable warnings, so that for example | |
| True+1 would eventually (in Python 3000) be illegal? | |
| => No. | |
| There's a small but vocal minority that would prefer to see | |
| "textbook" bools that don't support arithmetic operations at | |
| all, but most reviewers agree with me that bools should always | |
| allow arithmetic operations. | |
| 5) Should ``operator.truth(x)`` return an int or a bool? | |
| => bool. | |
| Tim Peters believes it should return an int, but almost all | |
| other reviewers agree that it should return a bool. My | |
| rationale: ``operator.truth()`` exists to force a Boolean context | |
| on its argument (it calls the C API ``PyObject_IsTrue())``. | |
| Whether the outcome is reported as int or bool is secondary; if | |
| bool exists there's no reason not to use it. (Under the PEP, | |
| ``operator.truth()`` now becomes an alias for ``bool()``; that's fine.) | |
| 6) Should bool inherit from int? | |
| => Yes. | |
| In an ideal world, bool might be better implemented as a | |
| separate integer type that knows how to perform mixed-mode | |
| arithmetic. However, inheriting bool from int eases the | |
| implementation enormously (in part since all C code that calls | |
| ``PyInt_Check()`` will continue to work -- this returns true for | |
| subclasses of int). Also, I believe this is right in terms of | |
| substitutability: code that requires an int can be fed a bool | |
| and it will behave the same as 0 or 1. Code that requires a | |
| bool may not work when it is given an int; for example, 3 & 4 | |
| is 0, but both 3 and 4 are true when considered as truth | |
| values. | |
| 7) Should the name 'bool' be changed? | |
| => No. | |
| Some reviewers have argued for boolean instead of bool, because | |
| this would be easier to understand (novices may have heard of | |
| Boolean algebra but may not make the connection with bool) or | |
| because they hate abbreviations. My take: Python uses | |
| abbreviations judiciously (like 'def', 'int', 'dict') and I | |
| don't think these are a burden to understanding. To a newbie, | |
| it doesn't matter whether it's called a waffle or a bool; it's | |
| a new word, and they learn quickly what it means. | |
| One reviewer has argued to make the name 'truth'. I find this | |
| an unattractive name, and would actually prefer to reserve this | |
| term (in documentation) for the more abstract concept of truth | |
| values that already exists in Python. For example: "when a | |
| container is interpreted as a truth value, an empty container | |
| is considered false and a non-empty one is considered true." | |
| 8) Should we strive to require that Boolean operations (like "if", | |
| "and", "not") have a bool as an argument in the future, so that | |
| for example "if []:" would become illegal and would have to be | |
| written as "if bool([]):" ??? | |
| => No!!! | |
| Some people believe that this is how a language with a textbook | |
| Boolean type should behave. Because it was brought up, others | |
| have worried that I might agree with this position. Let me | |
| make my position on this quite clear. This is not part of the | |
| PEP's motivation and I don't intend to make this change. (See | |
| also the section "Clarification" below.) | |
| Rationale | |
| ========= | |
| Most languages eventually grow a Boolean type; even C99 (the new | |
| and improved C standard, not yet widely adopted) has one. | |
| Many programmers apparently feel the need for a Boolean type; most | |
| Python documentation contains a bit of an apology for the absence | |
| of a Boolean type. I've seen lots of modules that defined | |
| constants "False=0" and "True=1" (or similar) at the top and used | |
| those. The problem with this is that everybody does it | |
| differently. For example, should you use "FALSE", "false", | |
| "False", "F" or even "f"? And should false be the value zero or | |
| None, or perhaps a truth value of a different type that will print | |
| as "true" or "false"? Adding a standard bool type to the language | |
| resolves those issues. | |
| Some external libraries (like databases and RPC packages) need to | |
| be able to distinguish between Boolean and integral values, and | |
| while it's usually possible to craft a solution, it would be | |
| easier if the language offered a standard Boolean type. This also | |
| applies to Jython: some Java classes have separately overloaded | |
| methods or constructors for int and boolean arguments. The bool | |
| type can be used to select the boolean variant. (The same is | |
| apparently the case for some COM interfaces.) | |
| The standard bool type can also serve as a way to force a value to | |
| be interpreted as a Boolean, which can be used to normalize | |
| Boolean values. When a Boolean value needs to be normalized to | |
| one of two values, ``bool(x)`` is much clearer than "not not x" and | |
| much more concise than | |
| :: | |
| if x: | |
| return 1 | |
| else: | |
| return 0 | |
| Here are some arguments derived from teaching Python. When | |
| showing people comparison operators etc. in the interactive shell, | |
| I think this is a bit ugly:: | |
| >>> a = 13 | |
| >>> b = 12 | |
| >>> a > b | |
| 1 | |
| >>> | |
| If this was:: | |
| >>> a > b | |
| True | |
| >>> | |
| it would require a millisecond less thinking each time a 0 or 1 | |
| was printed. | |
| There's also the issue (which I've seen baffling even experienced | |
| Pythonistas who had been away from the language for a while) that | |
| if you see:: | |
| >>> cmp(a, b) | |
| 1 | |
| >>> cmp(a, a) | |
| 0 | |
| >>> | |
| you might be tempted to believe that ``cmp()`` also returned a truth | |
| value, whereas in reality it can return three different values | |
| ``(-1, 0, 1)``. If ints were not (normally) used to represent | |
| Booleans results, this would stand out much more clearly as | |
| something completely different. | |
| Specification | |
| ============= | |
| The following Python code specifies most of the properties of the | |
| new type:: | |
| class bool(int): | |
| def __new__(cls, val=0): | |
| # This constructor always returns an existing instance | |
| if val: | |
| return True | |
| else: | |
| return False | |
| def __repr__(self): | |
| if self: | |
| return "True" | |
| else: | |
| return "False" | |
| __str__ = __repr__ | |
| def __and__(self, other): | |
| if isinstance(other, bool): | |
| return bool(int(self) & int(other)) | |
| else: | |
| return int.__and__(self, other) | |
| __rand__ = __and__ | |
| def __or__(self, other): | |
| if isinstance(other, bool): | |
| return bool(int(self) | int(other)) | |
| else: | |
| return int.__or__(self, other) | |
| __ror__ = __or__ | |
| def __xor__(self, other): | |
| if isinstance(other, bool): | |
| return bool(int(self) ^ int(other)) | |
| else: | |
| return int.__xor__(self, other) | |
| __rxor__ = __xor__ | |
| # Bootstrap truth values through sheer willpower | |
| False = int.__new__(bool, 0) | |
| True = int.__new__(bool, 1) | |
| The values ``False`` and ``True`` will be singletons, like None. Because | |
| the type has two values, perhaps these should be called | |
| "doubletons"? The real implementation will not allow other | |
| instances of bool to be created. | |
| ``True`` and ``False`` will properly round-trip through pickling and | |
| marshalling; for example ``pickle.loads(pickle.dumps(True))`` will | |
| return ``True``, and so will ``marshal.loads(marshal.dumps(True))``. | |
| All built-in operations that are defined to return a Boolean | |
| result will be changed to return ``False`` or ``True`` instead of 0 or 1. | |
| In particular, this affects comparisons (``<``, ``<=``, ``==``, ``!=``, | |
| ``>``, ``>=``, is, is not, in, not in), the unary operator 'not', the built-in | |
| functions ``callable()``, ``hasattr()``, ``isinstance()`` and ``issubclass()``, | |
| the dict method ``has_key()``, the string and unicode methods | |
| ``endswith()``, ``isalnum()``, ``isalpha()``, ``isdigit()``, ``islower()``, ``isspace()``, | |
| ``istitle()``, ``isupper()``, and ``startswith()``, the unicode methods | |
| ``isdecimal()`` and ``isnumeric()``, and the 'closed' attribute of file | |
| objects. The predicates in the operator module are also changed | |
| to return a bool, including ``operator.truth()``. | |
| Because bool inherits from int, True+1 is valid and equals 2, and | |
| so on. This is important for backwards compatibility: because | |
| comparisons and so on currently return integer values, there's no | |
| way of telling what uses existing applications make of these | |
| values. | |
| It is expected that over time, the standard library will be | |
| updated to use ``False`` and ``True`` when appropriate (but not to require | |
| a bool argument type where previous an int was allowed). This | |
| change should not pose additional problems and is not specified in | |
| detail by this PEP. | |
| C API | |
| ===== | |
| The header file "boolobject.h" defines the C API for the bool | |
| type. It is included by "Python.h" so there is no need to include | |
| it directly. | |
| The existing names ``Py_False`` and ``Py_True`` reference the unique bool | |
| objects ``False`` and ``True`` (previously these referenced static int | |
| objects with values 0 and 1, which were not unique amongst int | |
| values). | |
| A new API, ``PyObject *PyBool_FromLong(long)``, takes a C long int | |
| argument and returns a new reference to either ``Py_False`` (when the | |
| argument is zero) or ``Py_True`` (when it is nonzero). | |
| To check whether an object is a bool, the macro ``PyBool_Check()`` can | |
| be used. | |
| The type of bool instances is ``PyBoolObject *``. | |
| The bool type object is available as PyBool_Type. | |
| Clarification | |
| ============= | |
| This PEP does **not** change the fact that almost all object types | |
| can be used as truth values. For example, when used in an if | |
| statement, an empty list is false and a non-empty one is true; | |
| this does not change and there is no plan to ever change this. | |
| The only thing that changes is the preferred values to represent | |
| truth values when returned or assigned explicitly. Previously, | |
| these preferred truth values were 0 and 1; the PEP changes the | |
| preferred values to ``False`` and ``True``, and changes built-in | |
| operations to return these preferred values. | |
| Compatibility | |
| ============= | |
| Because of backwards compatibility, the bool type lacks many | |
| properties that some would like to see. For example, arithmetic | |
| operations with one or two bool arguments is allowed, treating | |
| ``False`` as 0 and ``True`` as 1. Also, a bool may be used as a sequence | |
| index. | |
| I don't see this as a problem, and I don't want evolve the | |
| language in this direction either. I don't believe that a | |
| stricter interpretation of "Booleanness" makes the language any | |
| clearer. | |
| Another consequence of the compatibility requirement is that the | |
| expression "True and 6" has the value 6, and similarly the | |
| expression "False or None" has the value None. The "and" and "or" | |
| operators are usefully defined to return the first argument that | |
| determines the outcome, and this won't change; in particular, they | |
| don't force the outcome to be a bool. Of course, if both | |
| arguments are bools, the outcome is always a bool. It can also | |
| easily be coerced into being a bool by writing for example "bool(x | |
| and y)". | |
| Resolved Issues | |
| =============== | |
| (See also the Review section above.) | |
| - Because the ``repr()`` or ``str()`` of a bool value is different from an | |
| int value, some code (for example doctest-based unit tests, and | |
| possibly database code that relies on things like "%s" % truth) | |
| may fail. It is easy to work around this (without explicitly | |
| referencing the bool type), and it is expected that this only | |
| affects a very small amount of code that can easily be fixed. | |
| - Other languages (C99, C++, Java) name the constants "false" and | |
| "true", in all lowercase. For Python, I prefer to stick with | |
| the example set by the existing built-in constants, which all | |
| use CapitalizedWords: ``None``, ``Ellipsis``, ``NotImplemented`` (as well as | |
| all built-in exceptions). Python's built-in namespace uses all | |
| lowercase for functions and types only. | |
| - It has been suggested that, in order to satisfy user | |
| expectations, for every x that is considered true in a Boolean | |
| context, the expression ``x == True`` should be true, and likewise | |
| if x is considered false, ``x == False`` should be true. In | |
| particular newbies who have only just learned about Boolean | |
| variables are likely to write | |
| :: | |
| if x == True: ... | |
| instead of the correct form, | |
| :: | |
| if x: ... | |
| There seem to be strong psychological and linguistic reasons why | |
| many people are at first uncomfortable with the latter form, but | |
| I believe that the solution should be in education rather than | |
| in crippling the language. After all, == is general seen as a | |
| transitive operator, meaning that from ``a==b`` and ``b==c`` we can | |
| deduce ``a==c``. But if any comparison to ``True`` were to report | |
| equality when the other operand was a true value of any type, | |
| atrocities like ``6==True==7`` would hold true, from which one could | |
| infer the falsehood ``6==7``. That's unacceptable. (In addition, | |
| it would break backwards compatibility. But even if it didn't, | |
| I'd still be against this, for the stated reasons.) | |
| Newbies should also be reminded that there's never a reason to | |
| write | |
| :: | |
| if bool(x): ... | |
| since the bool is implicit in the "if". Explicit is **not** | |
| better than implicit here, since the added verbiage impairs | |
| redability and there's no other interpretation possible. There | |
| is, however, sometimes a reason to write | |
| :: | |
| b = bool(x) | |
| This is useful when it is unattractive to keep a reference to an | |
| arbitrary object x, or when normalization is required for some | |
| other reason. It is also sometimes appropriate to write | |
| :: | |
| i = int(bool(x)) | |
| which converts the bool to an int with the value 0 or 1. This | |
| conveys the intention to henceforth use the value as an int. | |
| Implementation | |
| ============== | |
| A complete implementation in C has been uploaded to the | |
| SourceForge patch manager: https://bugs.python.org/issue528022 | |
| This will soon be checked into CVS for python 2.3a0. | |
| Copyright | |
| ========= | |
| This document has been placed in the public domain. | |
| .. | |
| Local Variables: | |
| mode: indented-text | |
| indent-tabs-mode: nil | |
| fill-column: 70 | |
| End: |