I'm assuming things like this are what you're talking about:

    def lazy_product2(*args, repeat=1):
        "recursive algorithm"
        args = args * repeat
        if not args:
            yield ()
            return
        for all_but_last in lazy_product2(*args[:-1]):
            for last in args[-1]():
                yield all_but_last + (last,)

    def lazy_product(*args, repeat=1):
        "non-recursive algorithm"
        funcs = args * repeat
        iterators = [iter(f()) for f in funcs]

        try:
            result = [next(it) for it in iterators]
        except StopIteration:
            return
        rev_range = range(len(iterators))[::-1]
        sentinel = object()

        while True:
            yield tuple(result)
            # get the next one
            for i in rev_range:
                result[i] = next(iterators[i], sentinel)
                if result[i] is sentinel:
                    iterators[i] = iter(funcs[i]())
                    result[i] = next(iterators[i], sentinel)
                    if result[i] is sentinel:
                        # No new data this time around
                        return
                else:
                    # stop "carrying", we incremented one.
                    # ready to yield.
                    break
            else: # no break
                return

    import itertools
    assert (list(lazy_product(lambda: range(3), lambda: range(2), repeat=2))
            == list(itertools.product(range(3), range(2), repeat=2))
            == list(lazy_product2(lambda: range(3), lambda: range(2), 
repeat=2)))

    infinite = lazy_product(itertools.count, lambda: "abc")
    print([next(infinite) for i in range(10)])
    # [(0, 'a'), (0, 'b'), (0, 'c'),
    #  (1, 'a'), (1, 'b'), (1, 'c'),
    #  (2, 'a'), (2, 'b'), (2, 'c'),
    #  (3, 'a')]

I think these don't generally fit with the "feel" of the itertools module 
though since almost everything there accepts iterables and returns iterators, 
and it's very focused on performance. Accepting no-arg callables that return 
iterators doesn't feel like an obvious API to me. Another option is to accept 
iterables that repeatedly get iter() called on them, but that's hard to make 
work with generators, and people confuse iterables and iterators anyway, so 
that might be too subtle. Maybe it's the sort of thing that just goes into a 
personal library of small utility functions.
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/NZSUEF3EJNU7YOTEYH3GUFY7M3K4QT5M/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to