[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