3

Suppose I want to create a function that takes an iterable. That iterable may contain other iterables to any level. I want to create a function that goes over these in order. So for example:

import collections
def it(l):
  for i in l:
    if isinstance(i, collections.Iterable):
      it(i) 
    else:
      print i


it([ [1, 2, 3], [[4, [5, 6]], 7], 8, [9, 10]])  

This produces the following output (as expected): 1 2 3 4 5 6 7 8 9 10

No supposed I want to do this with a generator. Why doesn't the following work as I would expect it to (essentially replacing the print statement with a yield) :

import collections
def it(l):
  for i in l:
    if isinstance(i, collections.Iterable):
      it(i) 
    else:
      yield i

Thanks!

  • Just a quick warning about using `collections.Iterable` in your type check: Strings will break your code. They are iterable, but their contents are other strings (the characters of the original string). This will lead you into an infinite recursion on the first character unless you put an extra check in to avoid it. You might also want to special case dictionaries (since they're iterable, but iterating only yields their keys). – Blckknght Oct 01 '13 at 23:55

1 Answers1

5

because when you recurse, you return a new generator -- But that generator never yields anything because you don't iterate over it. Instead, do something like:

def it(l):
  for i in l:
    if isinstance(i, collections.Iterable):
      for item in it(i):
        yield item
    else:
      yield i

Or, in python3.3, you can use the yield from keyword.

mgilson
  • 300,191
  • 65
  • 633
  • 696