How to join two generators or other iterables in Python?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Joining iterables in Python can mean different operations: concatenation, pairwise zipping, sorted merge, or alternating interleave. The correct tool depends on the output shape you need and whether laziness matters. For generator heavy code, using iterator primitives correctly keeps memory usage low and pipelines predictable.
Sequential Concatenation with itertools.chain
If you want all values from iterable A followed by all values from iterable B, use itertools.chain.
chain is lazy, so values are produced only when consumed.
When you have many iterables stored in a list, use chain.from_iterable:
This avoids unpacking large argument lists.
Custom Composition with yield from
For reusable custom behavior, create your own generator wrapper.
This is functionally similar to chain, but easy to extend with logging or conditional routing.
Pairwise Combination Is a Different Operation
Many questions about joining are actually about pairing elements by position. Use zip for that.
If iterables have different lengths and you want all elements, use zip_longest:
This is not concatenation. It changes output structure to tuples.
Sorted Merge for Already Sorted Streams
If both streams are sorted and you need one sorted output, use heapq.merge.
heapq.merge is lazy and avoids materializing or sorting the full combined list.
Interleaving Two Iterables
Sometimes you need alternating values from each iterable.
This pattern is useful in stream scheduling and display formatting.
Generator Exhaustion and Reuse
Generators are one pass iterators. Once consumed, they cannot be reused.
If you need repeated iteration, either recreate the generator or store results in a list intentionally.
Infinite Streams and Safe Consumption
When combining finite and infinite iterables, stay lazy and use bounded consumers such as islice.
Avoid converting infinite or very large joins into a full list.
Common Pitfalls
A frequent mistake is using zip when sequential concatenation was needed. The output shape becomes tuple pairs, which breaks downstream logic.
Another issue is exhausting a generator in debug prints, then seeing no values in actual processing.
Teams also materialize large pipelines too early with list(...), losing the memory advantage of iterator composition.
Summary
- Use
itertools.chainfor lazy sequential joining. - Use
yield fromwhen you want custom composable wrappers. - Use
ziporzip_longestfor pairwise alignment, not concatenation. - Use
heapq.mergefor lazy sorted merges. - Respect one pass generator behavior and keep pipelines lazy when possible.

