5

Anyways when debuging my code I found statement that basically subtracted boolean from float.

Then I tried following in python console:

>>> 15.0 - True 
14.0
>>> 15.0 - False
15.0

Can anyone explain to me:

jb.
  • 23,300
  • 18
  • 98
  • 136

3 Answers3

6

It is legal, because bool is a subclass of int:

>>> bool.__bases__
(<type 'int'>,)
>>> True == 1
True
>>> False == 0
True

Yes, it has some practical application. For example it was possible to do something like that before ternary statement was introduced:

result = [value_if_false, value_if_true][condition]

Which basically does what would be done in this code:

if condition:
    result = value_if_false
else:
    result = value_if_true

Other practical usages are:

  • you can sum several checks / booleans to receive number of results equaling True,
  • you can multiply by the result of the check:

    result = can_count_a * a + can_count_b * b
    
Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • What is the formal name for this? It's very similar to the ability to add a float to an int and have a float returned. – Mike Vella Sep 03 '13 at 21:12
  • @MikeVella: Formal name of what? Since `bool` is a subclass of `int`, when you are adding `bool` to `float`, the result is almost identical to adding `int` to `float`. Are you asking about _coercion_? – Tadeck Sep 03 '13 at 21:15
  • yes! *coercion* is just the term I was looking for - thanks! – Mike Vella Sep 03 '13 at 21:16
4
  • http://docs.python.org/3/library/stdtypes.html#boolean-values

    Boolean values are the two constant objects False and True. They are used to represent truth values (although other values can also be considered false or true).

    In numeric contexts (for example when used as the argument to an arithmetic operator), they behave like the integers 0 and 1, respectively.

  • not really nowadays, but you can write

    result = numeric_value * a_bool (this one is used a lot, for example, in shader languages)

    instead of

    result = numeric_value if a_bool else 0

    or

    result = (value_if_false, value_if_true)[a_bool]

    Don't do any of that though.

It's mostly what people with experience in lower-level languages expect, why take that away from them? In C, true and false are still macros for 1 and 0.

Before 2.3, there was no bool type in Python as well, so when it was introduced making it a subclass of int made sure no code was broken.

Pavel Anossov
  • 60,842
  • 14
  • 151
  • 124
  • I have done `result = (value_if_false, value_if_true)[a_bool]` when it is faster. great answer tho +1 – Noelkd Sep 03 '13 at 21:10
  • @Noelkd: faster than a ternary `if`? – Pavel Anossov Sep 03 '13 at 21:11
  • In some toy code when we where doing speed comps in the office yes, will have to wait till tomorrow for examples. ( edit: maybe not ternary if) – Noelkd Sep 03 '13 at 21:11
  • A similar answer to complement this one: http://stackoverflow.com/a/2764099/25450 (Strictly speaking, in Python2 `True` and `False` can be reassigned: `True, False = 2, 0; 15.0 - True` => `13.0`) – sastanin Sep 03 '13 at 21:20
  • disassembling a ternary operator call and @Noelkd 's expression: ternary operator performs 4 more calls when looking at bytecode mnemonics vs. 4 overall in the tuple-expression. – benjamin Sep 03 '13 at 21:28
  • @PavelAnossov it looks like it truely is faster. – benjamin Sep 03 '13 at 21:30
  • @benjamin I'd say it depends: http://pastebin.com/4XabHEBf – Pavel Anossov Sep 03 '13 at 21:31
  • :) that's what logic dictates: If there is a language construct, it should be optimized. Thanks for pointing that out. – benjamin Sep 03 '13 at 21:34
  • @benjamin: ternary `if` will also be much faster if it's expensive to evaluate `expression_is_false`, but `a_bool` turns out to be True. – Pavel Anossov Sep 03 '13 at 21:36
  • @PavelAnossov, I played a bit further around with timeit and sometimes tuple-notation would outperform ternary (n=30*10^6, 100*10⁶, multiple runs). I am not going to compare this in depth, but rather stay with the ternary. – benjamin Sep 03 '13 at 22:16
  • @PavelAnossov, http://pastebin.com/cA69xe0j, builds the tuple imho more efficient. But the ternary is still faster ~6.5% using timeit from the command-line with e.g.: python -m timeit -r 20 -v -n 5000000 "import speedcomp; speedcomp.array_access(False)". – benjamin Sep 03 '13 at 23:20
2

True evaluates to 1 and False evaluates to 0.

>>> True is 1
False
>>> True == 1
True
>>>

Bool is a subclass of int. As stated in PEP-285:

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.

This isn't of any much practical use and there are other answers with sudo examples of using bools. I thought it would be good to have some real examples:

f,b="Fizz","Buzz"
print "\n".join([["",f,b,f+b][(x%3==0) + 2*(x%5==0)] or str(x) for x in range(1,101)])

The section in question:

["",f,b,f+b][(x%3==0) + 2*(x%5==0)]

The selection of the return each line is based on two boolean expressions, if both are true we get (True) + 2*(True) which evaluates to 4 which is a fizzbuzz. Not too hard to understand once you get used to the idea that True == 1 and False == 0

Further more keeping with the theme:

print '\n'.join(['Fizz'*(not i%3) + 'Buzz'*(not i%5) or str(i) for i in range(1, 101)])

This example relies on what happens when you multiply strings in python:

>>> "Noelkd" * False
''

And that not True evaluates to 0:

>>> not True == 0
True

Uses for this fall into two categories:

  • Making harder to read code.

  • Competing in code golf competitions.

Noelkd
  • 7,686
  • 2
  • 29
  • 43