Implicit conversion to boolean in if and while statements

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Jul 16 03:52:04 EDT 2012


On Sun, 15 Jul 2012 22:03:52 -0700, Ranting Rick wrote:

> But if you are going to argue that "if obj" is *explicit enough*, then
> apply your argument consistently to "String"+1.75 also. Why must we be
> explicit about string conversion BUT not boolean conversion?

The problem with "String" + 1.75 is not lack of explicitness, but 
ambiguity. The + is operator is plenty explicit, but it is ambiguous when 
the operands have different types. Should it...?

- truncate "String" at the first non-digit (hence "") and then coerce
  it to 0.0, and hence return the float 1.75?

- coerce "String" to a float NaN on the basis that "String" is 
  not a number, and hence return NaN?

- coerce 1.75 to a string, and hence return "String1.75"?


The first behaviour is rather dubious, but a good case could be made for 
the second or third. Python takes a fourth approach, and refuses to allow 
such mixed type operations.

If + always meant "numeric addition", and & was used for string 
concatenation, then we could have unambiguous expressions using mixed 
types:

1 + 1.75  # int + float always coerces to float
1 + "2"   # int + str always coerces to int
1 & "2"   # int & str always coerces to str

but since & is used for integer bitwise-and, and + is used for both 
concatenation and addition, we can't, and so Python raises an exception.

For arithmetic, there is an unambiguous hierarchy of types, the numeric 
tower, which tells us which types coerce to what, e.g.:

int -> float -> complex

But there is no such hierarchy when it comes to (say) mixed strings and 
lists, etc., and hence Python raises an exception rather than guessing 
which type you wanted as the result.

This is completely irrelevant when it comes to bools -- we don't have to 
coerce a value into another type, we just need to know if it is something 
or nothing. The object itself is best able to make that decision, hence 
delegating it to a protocol and method:

- If the object is a container, and it has a length of 0, it is empty 
  and hence nothing (falsey); if it has a non-zero length, it is non-empty
  and hence something (truthy).

- Otherwise ask the container whether it is something or nothing by
  calling __nonzero__ (the original name) or __bool__.


Python makes a rather big blunder, at least from the perspective of 
consistency. Bools are ints:

py> issubclass(bool, int)
True
py> isinstance(True, int)
True
py> isinstance(False, int)
True

but there are things that can be converted into bools that can't be 
converted into ints, even though bools *are* ints! Contradiction.

py> x = [None, 42, '']
py> bool(x)
True
py> int(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'list'


Since x can be converted into True, and True == 1, you should be able to 
convert x into 1. But that's crazy, since x = [None, 42, ''].

*shrug* I don't call this a gotcha, but it is one of the more ugly 
consequences of Python's bool implementation.


> Can you
> reduce this to the absurd? Or will you just choose to ignore this valid
> point?

Mu. (Neither true nor false.)



-- 
Steven



More information about the Python-list mailing list