Python's any
and all
built-in functions are supposed to short-circuit, like the logical operators or
and and
do.
However, suppose we have a function definition like so:
def func(s):
print(s)
return True
and use it to build a list of values passed to any
or all
:
>>> any([func('s'), func('t')])
's'
't'
True
Since the list must be constructed before any
is called, the function is also evaluated ahead of time, effectively defeating the short-circuiting.
If the function calls are expensive, evaluating all the functions up front is a big loss and is a waste of this ability of any
.
Knowing that any
accepts any kind of iterable, how can we defer the evaluation of func
, so that the short-circuiting of any
prevents calling func(t)
?
We can use a generator expression, passing the functions and their arguments separately and evaluating only in the generator like so:
>>> any(func(arg) for arg in ('s', 't'))
's'
True
For different functions with different signatures, this could look like the following:
any(
f(*args)
for f, args in [(func1, ('s',)), (func2, (1, 't'))]
)
That way, any
will stop iterating over the generator as soon as one function call evaluates to True
, and that means that the function evaluation is fully lazy.
Another neat way to postpone the function evaluation is to use lambda expressions, like so:
>>> any(
... f()
... for f in [lambda: func('s'), lambda: func('t')]
... )
's'
True