I was just messing around in the Python interpreter and I came across some unexpected behavior.
>>> bools = (True, True, True, False)
>>> all(bools)
False
>>> any(bools)
True
Ok, so far nothing out of the ordinary...
>>> bools = (b for b in (True, True, True, False))
>>> all(bools)
False
>>> any(bools)
False
Here's where things start getting spooky. I figure this happens because the all
function iterates over the generator expression, calling its __next__
method and using up the values until it encounters one that is False
. Here's some evidence to back that theory up:
>>> bools = (b for b in (True, False, True, True))
>>> all(bools)
False
>>> any(bools)
True
I think the result is different because the False
is not at the end, so there are still some unused values left in the generator. If you type
>>> bools = (b for b in (True, False, True, True))
>>> all(bools)
False
>>> list(bools)
[True, True]
It seems like there are only 2 remaining values.
So, why exactly does this really happen? I'm sure there are many details that I'm missing.
If you want to return multiple values from a function, you can use generator functions with yield keywords. The yield expressions return multiple values. They return one value, then wait, save the local state, and resume again.
Python generators are a simple way of creating iterators. All the work we mentioned above are automatically handled by generators in Python. Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).
If you want to reuse this generator multiple times, you can use functools. partial. This will wrap the generator function in another function so each time you call func_with_yield() it creates the same generator function. Note: It also accepts function arguments for FunctionWithYield(args) if you have arguments.
List comprehensions are usually faster than generator expressions as generator expressions create another layer of overhead to store references for the iterator. However, the performance difference is often quite small.
The problem that you are having is that you are using the generator after it has produced all the values.
You can verify this by running the following code:
>>> bools = (b for b in (True, False, True, True))
>>> all(bools) # once the False is found it will stop producing values
True
>>> next(bools) # next value after False which is True
True
>>> next(bools) # next value after True which is True
True
>>> next(bools)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
This will work:
>>> bools = (b for b in (True, False, True, True))
>>> all(bools)
False
>>> bools = (b for b in (True, False, True, True))
>>> any(bools)
True
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With