[Python-ideas] Adding an optional function argument to all() and any() builtins

Ron Adam rrr at ronadam.com
Mon Nov 22 05:47:58 CET 2010



On 11/21/2010 10:19 PM, Steven D'Aprano wrote:
> Ron Adam wrote:
>> On 11/21/2010 01:39 PM, Andy Buckley wrote:
> [...]
>>> For example, this unclear code:
>>>
>>> testval = False
>>> for i in mylist:
>>> if mytestfunction(i):
>>> testval = True
>>> break
>>> if testval:
>>> foo()
>>
>> This has the advantage that it doesn't need to iterate the whole list.
>> Sometimes you can't beat a nice old fashion loop. ;-)
>
> Both any() and all() have lazy semantics and will stop as early as
> possible. They will only traverse the entire list if they have to. There's
> no need to write your own function to get that behaviour.
>
>
>> I would write it like this...
>>
>> def test_any(iterable, test=bool):
>> for i in iterable:
>> if test(i):
>> return True
>> return False
>
> That would be more easily written as:
>
> def test_any(iterable, test=bool):
> return any(test(obj) for obj in iterable)

Yep, you're right.

 >>> def test(value):
...   print(value)
...   return value > 0
...
 >>> my_list = list(range(-5, 5))
 >>> any(test(obj) for obj in my_list)
-5
-4
-3
-2
-1
0
1
True


I was thinking of this..

 >>> any([test(obj) for obj in my_list])
-5
-4
-3
-2
-1
0
1
2
3
4
True

This is a nice example of the differences between a generator expression 
and a list comprehension.


> But of course that raises the question of what advantage test_any() has
> over an inline call to any(test(obj) for obj in iterable).
>
>
> Aside: for what little it's worth, defaulting to bool as the test function
> causes a significant slow-down:
>
> [steve at sylar ~]$ python3 -m timeit -s "L=[0]*1000000" "any(L)"
> 10 loops, best of 3: 28 msec per loop
> [steve at sylar ~]$ python3 -m timeit -s "L=[0]*1000000" "any(bool(x) for x in
> L)"
> 10 loops, best of 3: 615 msec per loop
>
>
> In fairness, some of that slow-down is due to the overhead of the generator
> expression itself:
>
> [steve at sylar ~]$ python3 -m timeit -s "L=[0]*1000000" "any(x for x in L)"
> 10 loops, best of 3: 241 msec per loop
>
> but it's best to avoid calling bool() unless you absolutely need an actual
> bool. test_any should probably be written as:
>
> def test_any(iterable, test=None):
> if test: return any(test(obj) for obj in iterable)
> else: return any(iterable)
>
>
> although my care-factor is insufficient to actually bother timing it :)

I agree. ;-)

Ron




More information about the Python-ideas mailing list