Why does presence of value evaluate as “Truthy” in Python? [e.i. Types evaluate True in conditionals]

softwareengineering.stackexchange https://softwareengineering.stackexchange.com/questions/295354

  •  10-10-2020
  •  | 
  •  

Вопрос

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?

Это было полезно?

Решение

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.

Другие советы

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 ;-)

Лицензировано под: CC-BY-SA с атрибуция
scroll top