[Chicago] duck typing to handle a string or iterable of strings

John Hunter jdh2358 at gmail.com
Fri May 20 18:18:20 CEST 2011


On Fri, May 20, 2011 at 10:58 AM, Michael Tobis <mtobis at gmail.com> wrote:

> OK, but still.
>
> I have also faced this problem.
>
> Flattening nested lists which contain strings is an example.
>
> Because a string is an iterable, if we want a method that tolerates a
> single string or a list of strings, treating the single string the
> same as a list with one element, we have to be able to treat the
> string as a non-iterable. The question is what the most elegant way to
> do this is if we are building general purpose utility functions that
> we don;'t want to violate duck typing.  I recall punting, but I also
> recall wondering the same thing.
>
> So, consider a routine that is intended to flatten a nested list. We
> want to write a recursive routine that takes [1,[2,3,[4,5],"fred"]] to
> [1,2,3,4,5,"fred"] and not to [1,2,3,4,5,"f","r","e","d"]. And even
> the latter is not as easy as you'd expect.
>
> It totally violated least surprise for me when I did something like
>
> >>> def atomprint(thing):
> ...     try:
> ...         for item in thing:
> ...             atomprint(item)
> ...     except TypeError:
> ...         print thing
>
> It works beautifully for lists of integers, floats, various other
> things. I felt satisfied and ever so pythonic. But it overflows the
> stack when you pass it a non-empty string!
>


matplotlib.cbook defines "flatten" with a scalar customizable scalarp
function to do what you want (the default returns True on non-iterables and
strings).  The module defines


def is_scalar_or_string(val):
    return is_string_like(val) or not iterable(val)

def flatten(seq, scalarp=is_scalar_or_string):
    """
    this generator flattens nested containers such as

    >>> l=( ('John', 'Hunter'), (1,23), [[[[42,(5,23)]]]])

    so that

    >>> for i in flatten(l): print i,
    John Hunter 1 23 42 5 23

    By: Composite of Holger Krekel and Luther Blissett
    From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/121294
    and Recipe 1.12 in cookbook
    """
    for item in seq:
        if scalarp(item): yield item
        else:
            for subitem in flatten(item, scalarp):
                yield subitem



When applied to your example, it yields

In [49]: import matplotlib.cbook as cbook


In [51]: for row in cbook.flatten ([1,[2,3,[4,5],"fred"]] ):
   ....:     print row
   ....:
   ....:
1
2
3
4
5
fred
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/chicago/attachments/20110520/49df14a0/attachment-0001.html>


More information about the Chicago mailing list