Recently, reading Python \"Functional Programming HOWTO\", I came across a mentioned there test_generators.py
standard module, where I found the following gener
This seems to work, and it's still lazy:
def conjoin(gs):
return [()] if not gs else (
(val,) + suffix for val in gs[0]() for suffix in conjoin(gs[1:])
)
def range3():
return range(3)
print list(conjoin([range3, range3]))
Output:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
Example usage to show mutable state:
x = ""
def mutablerange():
global x
x += "x"
return [x + str(i) for i in range(3)]
print list(conjoin([range3, mutablerange]))
Output: (watch the increasing number of 'x's)
[(0, 'x0'), (0, 'x1'), (0, 'x2'), (1, 'xx0'), (1, 'xx1'), (1, 'xx2'), (2, 'xxx0'), (2, 'xxx1'), (2, 'xxx2')]
And if we use itertools.product
:
x = ""
print list(itertools.product(range3(), mutablerange()))
the result is the following:
[(0, 'x0'), (0, 'x1'), (0, 'x2'), (1, 'x0'), (1, 'x1'), (1, 'x2'), (2, 'x0'), (2, 'x1'), (2, 'x2')]
So, one clearly see, that itertools.product
caches the values returned by the iterator.