itertools.flatten()? and copying generators/iterators.

Francis Avila francisgavila at yahoo.com
Thu Oct 30 08:11:19 EST 2003


"Peter Otten" <__peter__ at web.de> wrote in message
news:bnqrni$mmn$05$1 at news.t-online.com...
> I think your flatten() is getting more complex fast, and you're reaching
the
> point where you should either use a version designed for the special
> purpose or pay the performance price and go with a simple test function
> like my keepstrings() somewhere above.

Actually, with your suggestion, it's gotten much simpler. See below.

> PS: The Martelli virus spreading fast: have you timed it recently?

I don't have time now, but I'll collect all the functions and time them all.
There are two groups though: those that can modify as they go (i.e. contain
custom type handlers), and those that don't, so I need two sets of test
cases, and to ensure that they all can at least pass the static tests.  And
there are a lot of functions.  But now I'm really curious to see their times
and the power/time tradeoff.

Anyway, final revision.  Much nicer (I took out the doctests cases for the
post. They're the same anyway, with one minor addition which I also added to
flatten_dictcond.)

--- Code ---

def defaulthandler(seq):
    """Return iterability of seq and new sequence.

    Iterability is either a bool (which asserts class-instance
    iterability), or a function which accepts a sequence and returns
    (instance-iterability, newseq).  The function asserts
    instance-iterability.

    Iterability as returned from default handler is *always* cached
    into do_descend, so for every call to this function, seq will have
    a unique type.

    I can't imagine that your own handler would *ever* need to modify
    seq before returning, unless you're doing some serious voodoo. You
    probably just need to add a type:function pair to do_descend.

    """
    # It is impossible to avoid an exception test, as far as I can
    # tell. Oh well.
    try:
        iter(seq)
    except TypeError:
        return False, seq
    else:
        return True, seq


def flatten_dictdef(iterable, do_descend=None,
defaulthandler=defaulthandler):
    """Yield leaves of iterable.

    Pretty much same semantics as flatten_dictcond.

    Function items in do_descend determine if iterable is instance-iterable.
        They return (iterability, newsequence), the iterability of the
        *NEW*SEQUENCE* (like normal).

    Boolean items in do_descend determine if iterable is
    class-iterable.

    Finally, defaulthandler determines any-iterability.
        defaulthandler returns (iterability, newsequence), the
        iterability of the *ORIGINAL*SEQUENCE*. (VERY important!)

    """
    ## Preload cache with atomic strings per default.
    if do_descend is None:
        do_descend = {type(''):False, type(u''):False}

    descend = do_descend.get(iterable.__class__, defaulthandler)

    # Of course, we end up *testing* in the reverse order of what the
    # docstring outlines.

    if descend is defaulthandler:
        # Oy, I was going to write this:

        # do_descend[iterable.__class__], iterable = defaulthandler(descend)

        # But I don't remember the order of evaluation, and I'm too
        # tired to look it up...

        isit, s = descend(iterable)
        descend = do_descend[iterable.__class__] = isit
        iterable = s

    elif callable(descend):
        descend, iterable = descend(iterable)
    #Else, bool constant.

    if not descend:
        yield iterable
    else:
        for elem in iterable:
            for subelem in flatten_dictdef(elem, do_descend,
defaulthandler):
                yield subelem
    # Ah, nice! This one I'm keeping.


--
Francis Avila





More information about the Python-list mailing list