title: zip iterators asserting for equal length in python
body: <p>I am looking for a nice way to <code>zip</code> several iterables raising an exception if the lengths of the iterables are not equal.</p>
  <p>In the case where the iterables are lists or have a <code>len</code> method this solution is clean and easy:</p>
  <pre><code>def zip_equal(it1, it2):
      if len(it1) != len(it2):
          raise ValueError(&quot;Lengths of iterables are different&quot;)
      return zip(it1, it2)
  </code></pre>
  <p>However, if <code>it1</code> and <code>it2</code> are generators, the previous function fails because the length is not defined <code>TypeError: object of type 'generator' has no len()</code>.</p>
  <p>I imagine the <a href="https://docs.python.org/3/library/itertools.html" rel="noreferrer"><code>itertools</code></a> module offers a simple way to implement that, but so far I have not been able to find it. I have come up with this home-made solution:</p>
  <pre><code>def zip_equal(it1, it2):
      exhausted = False
      while True:
          try:
              el1 = next(it1)
              if exhausted: # in a previous iteration it2 was exhausted but it1 still has elements
                  raise ValueError(&quot;it1 and it2 have different lengths&quot;)
          except StopIteration:
              exhausted = True
              # it2 must be exhausted too.
          try:
              el2 = next(it2)
              # here it2 is not exhausted.
              if exhausted:  # it1 was exhausted =&gt; raise
                  raise ValueError(&quot;it1 and it2 have different lengths&quot;)
          except StopIteration:
              # here it2 is exhausted
              if not exhausted:
                  # but it1 was not exhausted =&gt; raise
                  raise ValueError(&quot;it1 and it2 have different lengths&quot;)
              exhausted = True
          if not exhausted:
              yield (el1, el2)
          else:
              return
  </code></pre>
  <p>The solution can be tested with the following code:</p>
  <pre><code>it1 = (x for x in ['a', 'b', 'c'])  # it1 has length 3
  it2 = (x for x in [0, 1, 2, 3])     # it2 has length 4
  list(zip_equal(it1, it2))           # len(it1) &lt; len(it2) =&gt; raise
  it1 = (x for x in ['a', 'b', 'c'])  # it1 has length 3
  it2 = (x for x in [0, 1, 2, 3])     # it2 has length 4
  list(zip_equal(it2, it1))           # len(it2) &gt; len(it1) =&gt; raise
  it1 = (x for x in ['a', 'b', 'c', 'd'])  # it1 has length 4
  it2 = (x for x in [0, 1, 2, 3])          # it2 has length 4
  list(zip_equal(it1, it2))                # like zip (or izip in python2)
  </code></pre>
  <p>Am I overlooking any alternative solution? Is there a simpler implementation of my <code>zip_equal</code> function?</p>
  <p><strong>Update:</strong></p>
  <ul>
  <li>Requiring python 3.10 or newer, see Asocia's <a href="https://stackoverflow.com/a/62773251/446149">answer</a></li>
  <li>Thorough performance benchmarking and best performing solution on python&lt;3.10: Stefan's <a href="https://stackoverflow.com/a/69485272/446149">answer</a></li>
  <li>Simple answer without external dependencies:
  Martijn Pieters' <a href="https://stackoverflow.com/a/32954700/446149">answer</a> (please check the comments for a bugfix in some corner cases)</li>
  <li>More complex than Martijn's, but with better performance: cjerdonek's <a href="https://stackoverflow.com/a/40596355/446149">answer</a></li>
  <li>If you don't mind a package dependency, see
  pylang's <a href="https://stackoverflow.com/a/61832383/446149">answer</a></li>
  </ul>