40
>>> class BOOL(bool):
...     print "why?"
... 
why?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    type 'bool' is not an acceptable base type

I thought Python trusted the programmer.

Juanjo Conti
  • 28,823
  • 42
  • 111
  • 133

6 Answers6

59

Guido's take on it:

I thought about this last night, and realized that you shouldn't be allowed to subclass bool at all! A subclass would only be useful when it has instances, but the mere existance of an instance of a subclass of bool would break the invariant that True and False are the only instances of bool! (An instance of a subclass of C is also an instance of C.) I think it's important not to provide a backdoor to create additional bool instances, so I think bool should not be subclassable.

Reference: http://mail.python.org/pipermail/python-dev/2002-March/020822.html

Max Shawabkeh
  • 37,799
  • 10
  • 82
  • 91
  • 21
    True and False are singleton. This is a better answer. – Juanjo Conti Jan 31 '10 at 15:36
  • 1
    Good to know things are consistent: `class Foo(None.__class__): ...` -> `TypeError: type 'NoneType' is not an acceptable base type` – Wayne Werner Feb 01 '17 at 14:43
  • 2
    trying to understand this answer: inheriting from `bool` would not affect the invariability of `True` and `False` in any way. an instance of the subclass would still be either `True` or `False` (from a `bool` perspective). it may have other attributes and may or may not be equal to one of them if you override `__eq__()` but otherwise it doesn't negatively affect `bool` or its two possible values. what am i missing? – user3204459 Jul 03 '19 at 17:50
  • @user3204459 when testing the values like `True`, `False`, or `None` we can use identity operation `is` because we know these values to be singletons. Much code already relies on this trick, so subclassing these types would break these identity based checks. – flakes Apr 20 '20 at 16:42
  • @flakes sorry, i still don't get it. `True` and `False` will still be `bool` and will only test against `True` and `False` when using `is`. so what? what is broken by that? can you give a concrete example? why is `False` singleton in a way that `0` is not? – user3204459 Apr 20 '20 at 20:13
  • 1
    @user3204459 If you inherit bool and then pass an instance of this new class type to a method, then the new instance will break existing boolean logic (you cannot satisfy Liskov substitution principle for inheriting bool). Never before has it been possible to have an instance of bool that doesn't respect identity checks. You break basic language predicates like `if b and isinstance(b, bool) then b is True`. This will cause you code to follow paths that we're not intended by the library designers, and just generally make the language feel unstable. – flakes Apr 21 '20 at 14:03
  • @flakes but that's true for any class inheritance (consider for instance `if not b and isinstance(b, int) then b is 0` with a class inheriting from `int`). what's so special about `bool`? inheriting from `bool` shouldn't break anything, everything will still be doing what it was meant to do. `==` will call `__eq__()` and `is` will check identity, so people who use the right operator will get the correct behavior. people using identity instead of equality or vice versa seems like a strange reason to disallow some arbitrary thing in a programming language, because anything can be misused – user3204459 Apr 21 '20 at 23:28
  • @user3204459 Your example is not correct. integers can be subclassed and can be equal while having separate identities. (i.e. `a = True; b = True; a is b` will always hold, but the same example with `int` can fail https://stackoverflow.com/a/306353/3280538) An easier way to rationalize this could be to think of bool as an Enum (you can't subclass an Enum either). Normally code does not call `==` on bools or Enums because it is slower than doing the identity check. As you mentioned, `a == True` will invoke `__eq__()` which is slower than `a is True` which essentially does `id(a) == id(True)` – flakes Apr 22 '20 at 00:16
  • @flakes but that was exactly the point of my example -- if we can live with equal value with separate identity with `int`, why can't we with `bool`? what makes `bool` different? writing `is` instead of `==` for the slight speed advantage can also be done with `int` so people trying to shave off a few clock cycles might write `x is 3` instead of `x == 3` but python does allow inheriting from `int` (which would "break" `x is 3`). not allowing inheriting from `enum.Enum` is also silly, especially since you can work around this restriction quite easily by defining a member... – user3204459 Apr 22 '20 at 07:12
12

If you are using Python 3, and you want to have a class that can be evaluated as a boolean, but also contain other functionality, implement __bool__ in your class.

In Python 2 the same effect can be achieved by implementing __nonzero__ or __len__ (if your class is a container).

Oleh Prypin
  • 33,184
  • 10
  • 89
  • 99
AJ.
  • 27,586
  • 18
  • 84
  • 94
12

Since the OP mentions in a comment:

I want 1 and 2 to return an instance of my class.

I think it's important to point out that this is entirely impossible: Python does not let you alter built-in types (and, in particular, their special methods). Literal 1 will always be an instance of built-in type int, and in any case the basic semantics of the and operator are not overridable anyway -- a and b is always identical to b if a else a for any a and b (no bool coercion involved, even though the OP appears to mistakenly believe one is happening).

Restating this crucial point: the value of a and b is always, unchangeably either a or b -- there is no way to break this semantic constraint (even if a and b were instances of your own peculiar classes -- even less so of course when they're constrained to be instances of Python's built-in int!-).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • Impossible to alter built-in types? Isn't this what the [forbiddenfruit library](https://clarete.li/forbiddenfruit/) does? – gerrit Apr 16 '20 at 09:22
9

Here is a post that explains the reasoning behind the decision: http://mail.python.org/pipermail/python-dev/2004-February/042537.html

The idea is that bool has a specific purpose - to be True or to be False, and adding to that would only serve to complicate your code elsewhere.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
danben
  • 80,905
  • 18
  • 123
  • 145
3

Because bool is supposed to only have two values -- True and False. If you were able to subclass bool, you could define arbitrary numbers of values for it, and that's definitely not what you want to happen.

A better question is: why do you want to extend bool?

John Feminella
  • 303,634
  • 46
  • 339
  • 357
  • I'd like to add an attribute that let me track some things on the object. – Juanjo Conti Jan 31 '10 at 15:34
  • 4
    @Juanjo then what you want is not a boolean, it is a tuple of a boolean and something else. A boolean must be either a True or a False, no other values and no other attributes that would make an instance of that class to compare negatively with both True and False. It that was to happen, it wouldn't be a boolean anymore. – Juliano Jan 31 '10 at 15:56
  • All right, but I can't override int.__nonzero__ to return this tuple. Can I? – Juanjo Conti Jan 31 '10 at 15:59
  • Answer: fuzzy logic, overloading a operator in bool, etc. – tox123 Dec 17 '14 at 02:05
  • Overloading magic methods and adding extra methods, while keeping boolean operations consistent. Ie, sing a couple of mixins to have a Boolean class that can be readily converted to/from ctypes and to/from string in a way that is compliant with a C custom network protocol API. – Tristan Duquesne Oct 27 '21 at 18:07
0

I think there is no particularly good reason to prohibit subclassing bool.

Guido said (as quoted in the top-voted, accepted answer) that it preserves the invariant that True and False are the only instances of bool. But that is not true:

>>> class BOOL:
...     @property
...     def __class__(self):
...         return bool
...
>>> FileNotFound = BOOL()
>>> isinstance(FileNotFound, bool)
True
>>>

This behavior of __class__ is documented and used in the standard library.

I guess you could argue that the prohibition on subclassing still preserves some kind of "moral" closedness of bool, but it's not clear to me what good that is. Reading Guido's rationale, a reasonable Python programmer would conclude he's saying that isinstance(x, bool) is a safe equivalent of x is True or x is False, which it isn't.

Furthermore, if the exact type matters to you, you should be testing type(x) is T, not isinstance(x, T), in any case. Making those expressions equivalent in the one case of bool—even if they succeeded in doing it—would just encourage the writing of code that doesn't clearly express intent, in that one case.

(type(FileNotFound) is BOOL, not bool, so the exact-type test does work here. A malicious actor could, I suppose, replace type. Generally, expecting any sort of invariant to hold in Python is a fool's errand.)

benrg
  • 1,395
  • 11
  • 13