def flatten( i ): try: i = i.__iter__() while 1: j = flatten( i.next() ) try: while 1: yield j.next() except StopIteration: pass except AttributeError: yield i
Probably you want to catch a TypeError instead of an AttributeError; objects may support the iterator protocol without defining an __iter__ method:
>>> class C: ... def __getitem__(self, index): ... if index > 3: ... raise IndexError(index) ... return index ... >>> list(iter(C())) [0, 1, 2, 3]
I would write your code as something like:
>>> def flatten(i): ... try: ... if isinstance(i, basestring): ... raise TypeError('strings are atomic') ... iterable = iter(i) ... except TypeError: ... yield i ... else: ... for item in iterable: ... for sub_item in flatten(item): ... yield sub_item ... >>> list(flatten([['N','F'],['E'],['D']])) ['N', 'F', 'E', 'D'] >>> list(flatten([C(), 'A', 'B', ['C', C()]])) [0, 1, 2, 3, 'A', 'B', 'C', 0, 1, 2, 3]
Note that I special-case strings because, while strings support the iterator protocol, in this case we want to consider them 'atomic'. By catching the TypeError instead of an AttributeError, I can support old-style iterators as well.
Steve -- http://mail.python.org/mailman/listinfo/python-list