0

Example:

if float: print('float is true?')

returns:

enter image description here

Why do all objects other than ones explicitly defined as False or "False" evaluate truthfully?

1

2 Answers 2

3

The Python language is strongly influenced by C. And C is a horrible language that didn't even have a boolean type until C99. Instead, it has integers. Anything that looks like a zero integer is false, anything else is true. It turns out that this is mightily convenient.

Wanna test for a null pointer? Unlike Java, you don't have to spell out if (thing != null) foo(thing); – a if (thing) foo(thing); suffices. Wanna check if we reached the end of a string? Just dereference it in a conditional:

char c;
while ((c = *str++)) do_something(c);

Wanna check if an array contains any items? Just test for the length:

int n;
...;
if (n) we_have_items();

So conceptually, a conditional has two forms:

  • a predicate form with some expression that evaluates to a boolean-interpretable value.
  • a container form with a single variable to check that this container is not-empty.

While Python has actual boolean types rather than overloading integers, it keeps this very popular mechanism for collections. For example, None (Python's null equivalent), empty strings "", empty lists [], and empty dicts {} all evaluate to False in a boolean context. This usually does what you want, and if it doesn't, you can be explicit about your requirements. For example, you can specifically test that a variable is not None:

if x is not None:
  do_something_with(x)

However, if you know that name is an optional string, then writing

if name:
  print("hello, ", name)
else:
  print("name required!")

is much more comfortable than

if bool(name):  # manually force to-bool conversion
  ...

or

if name is not None and len(name):  # explicitly state your intent: at least one character
  ...

or

if name is not None and bool(len(name)): # what the above example really means
   ...

or

if name is not None and len(xs) > 0:  # even more explicit
  ...

It is important to note that these implicit conversions are not black magic that pull the comfy rug of type safety away from under your feet. In Python, these conversions always happen quite explicitly, but if you pass a function or a builtin some value, it may perform any such conversions on that argument. For example, you can explicitly request a bool conversion via bool(some_value). So if you want to, you can think of the if EXPRESSION: ... builtin as being implemented like:

def if(EXPRESSION, then, else):
  real_if bool(EXPRESSION):
    then()
  real_else:
    else()

You can also define your own to-bool conversion for your own classes, by implementing the special __bool__(self) method. But careful! Conversions and operator overloading can get quite confusing for a user unless the semantics are obvious and intuitive. In Python, it is considered sufficiently clear for a to-bool conversion to test whether a collection or container is empty. For non-containers, it might be a good idea to use an ordinary, named method instead.

So why does it make sense for values to evaluate to True even when no to-bool conversion has been defined? User-defined types generally have reference semantics, i.e. the variable holds a pointer to the value, not the value itself. As we have seen, the if (pointer) ... C idiom tests whether a pointer is not empty, so it makes some sense to extend it even to Python which does not have reified pointers. So the conditional here does not test whether the value is true or false, but whether the variable currently holds a value. If the type had value semantics, I would find such a to-bool conversion highly confusing, but that might just be the C++-programmer in me speaking.

-1

Why do all objects other than ones explicitly defined as False or "False" evaluate truthfully?

Simple explanation:

Because they do, by definition. Sorry if that seems like a tautology, but that really is the correct answer: this is the arbitrary way it works because that is the arbitrary way it is specified to work in the arbitrary, canonical rules of the language.

Philosophical explanation:

Have a look at the Zen of Python, which states:

Special cases aren't special enough to break the rules.

If the rules are that "this specific set of values are falsy and everything else is truthy," then even though it seems nonsensical to have a reference to a type being either truthy or falsy, that's a special case, and it's not special enough to break the rules.

Flame bait explanation:

Because Python lacks a proper static type system, in which the only True and False values would be True and False, and anything else would be rejected at compile time. And since you can't do that in a dynamic language, Python takes the easy way out instead of spending extra effort to do it "the right way" (and good luck defining that in this context!) even when it means accepting obviously-invalid and nonsensical code.

Take your pick ;-)

9
  • So it is advantageous when writing a language to define rules and only worry about certain known cases of instances where those rules are broken and cause problems? Rather than to only allow certain known instances to work?
    – ruben_KAI
    Sep 3, 2015 at 21:07
  • 3
    Huh? What does static typing have to do with implicit conversions? Many very static languages have them as well, e.g. C++ or Scala. Or do you also dislike operator overloading because it's so unobtrusive? In Python, you still have the option to distinguish whether something is just true-ish or actually true itself: if x: ... vs. if x is True: .... Compared with Perl, Bash, or PHP, the Python language actually has a usable and clean type system – if you only knew it. -1.
    – amon
    Sep 3, 2015 at 21:36
  • 3
    "Because Python lacks a proper static type system, in which the only True and False values would be True and False, and anything else would be rejected at compile time.": I do not think the reason is dynamic typing. One could throw an exception each time a boolean is expected and something else is provided, in the same way as adding a number and a string throws.
    – Giorgio
    Sep 3, 2015 at 21:44
  • 1
    @BenjaminHodgson: I did not want to argue about the usefulness of Python's solution. My point was that this solution is not enforced by the dynamic type system. For many data types that are monoids (boolean with or, integers with +, strings with +), it seems intuitive to choose the identity element (False, 0, '') as false and all other elements as true.
    – Giorgio
    Sep 4, 2015 at 8:53
  • 1
    @EricRenouf What it means is that if your Python program is complex enough that it can run for an hour before it hits the line of code in question, you won't know anything's wrong until you've been running it for an hour and then suddenly it throws a TypeError in your face. If it had a compiler checking for things like that, you'd know before the program ever even starts up, minimizing aggravation and the chance of lost work. Sep 4, 2015 at 13:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.