List Flatten

Alex Martelli aleaxit at yahoo.com
Mon Oct 18 05:37:44 EDT 2004


Raymond Hettinger <vze4rx4y at verizon.net> wrote:
   ...
> >>> def f(n):
>     iterstack = [iter(n)]
>     while iterstack:
>         for elem in iterstack[-1]:
>             try:
>                 it = iter(elem)
>             except TypeError:
>                 pass
>             else:
>                 if not isinstance(elem, basestring):
>                     iterstack.append(it)
>                     break
>             yield elem
>         else:
>             iterstack.pop()  # remove iterator only when it is exhausted

Nice!  I was wondering about a sligthly different ordering...:

def flatten(sequence, isatomic=None):
    if isatomic is None:
        def isatomic(item): return isinstance(item, basestring)
    stack = [iter(sequence)]
    while stack:
        for item in stack[-1]:
            if not isatomic(item):
                try: stack.append(iter(item))
                except TypeError: pass
                else: break
            yield item
        else:
            stack.pop()

Very similar, of course, but for some reason it looks nicer to me to
enter the try/except/else only after "singling out" items that are meant
to be taken as "atomic" no matter what (by default, all strings).  I'm
not sure how to weigh the pro's and con's of the two very similar
variations.  Maybe the second one may be preferred on a rather minor
aspect: if I'm flattening a sequence which, I suspect, will contain lots
of numbers at the leaves, I could code and pass in something like:

def isatomic(item):
    return isinstance(item, (basestring, int, long, float, complex))

and bypass the try/except for all those numbers, perhaps speeding things
up...?  (unmeasured...).  BTW, this does point out how nice a
baseinteger and/or basenumber analog to basestring would be, to shorten
that longish tuple of types.  Ah well, maybe in 2.5!-)


Alex



More information about the Python-list mailing list