Why does python `any` return a bool instead of the value?
Solution 1:
This very issue came up up on the Python developer's mailing list in 2005, when Guido Van Rossum proposed adding any
and all
to Python 2.5.
Bill Janssen requested that they be implemented as
def any(S):
for x in S:
if x:
return x
return S[-1]
def all(S):
for x in S:
if not x:
return x
return S[-1]
Raymond Hettinger, who implemented any
and all
, responded specifically addressing why any
and all
don't act like and
and or
:
Over time, I've gotten feedback about these and other itertools recipes. No one has objected to the True/False return values in those recipes or in Guido's version.
Guido's version matches the normal expectation of any/all being a predicate. Also, it avoids the kind of errors/confusion that people currently experience with Python's unique implementation of "and" and "or".
Returning the last element is not evil; it's just weird, unexpected, and non-obvious. Resist the urge to get tricky with this one.
The mailing list largely concurred, leaving the implementation as you see it today.
Solution 2:
and
and or
can be sensibly defined in a way that they always return one of their operands. However, any
and all
cannot sensibly be defined always to return a value from their input sequence: specifically they cannot do so when the list is empty. Both any
and all
currently have a well defined result in this situation: any
returns False and all
returns True. You would be forced to sometimes return a boolean value and sometimes return an item from the sequence, which makes for an unpleasant and surprising interface. Much better to be simple and consistent.
Solution 3:
Starting Python 3.8
, and the introduction of assignment expressions (PEP 572) (:=
operator), we can alternatively explicitly capture a witness of an any
expression or a counterexample of an all
expression:
To quote a couple examples from the PEP description:
if any(len(long_line := line) >= 100 for line in lines):
print("Extremely long line:", long_line)
if all((nonblank := line).strip() == '' for line in lines):
print("All lines are blank")
else:
print("First non-blank line:", nonblank)