March 12, 2009 will

Nesting Instinct

I figured I'd start a series of Python challenges in my blog. Whenever I've posted questions such as these in the past, the discussion that follows is always very entertaining!

A discussion arose today at work about the best way to flatten a tuple consisting of values and other tuples (which may be nested arbitrarily). So the tuple (1, (1, 2, (1, 2, 3), 3)) would become (1, 1, 2, 1, 2, 3, 3). Now there was actually code implemented which was functional, efficient and easy to read – but where is the fun in that?

I figured I could re-write it in a more terse manner, and here is what I came up with:

def flatten(tpl):
    return eval(repr(tpl).replace('(', '').replace(')', ''))

t= (1, (1, 2, (1, 2, 3), 3))
print flatten(t)

This flatten function would actually work in the context of our app, but I would never use it in production code. My challenge today, is to tell me why this code should never be used!

Use Markdown for formatting
*Italic* **Bold** `inline code` Links to [Google](http://www.google.com) > This is a quote > ```python import this ```
your comment will be previewed here
gravatar
pythonista
You could have string values (for example) with parenthesis in them. These values would be changed.
gravatar
Steve Holden
A further reason is that you have no idea what the repr of the object looks like, since you don't even examine its type …

One must hope nobody starts devising malicious objects with exploitive repr()s.

Cute idea, though.
gravatar
Euan Goddard
I can't believe you had the audacity to post that! Very amusing though. Let me know when you can properly do it in 2 lines
gravatar
WIll McGugan
How about this, Euan…

 def flatten(tpl):
    return sum(map(flatten, tpl), ()) if type(tpl) is tuple else (tpl,)
    
t= (1, (1, 2, (1, 2, 3), 3))
print flatten(t)

Not quite as concise as the the other flatten, but less of a crime against Python!
gravatar
jdm
2euan: is this “properly”?
 def flatten(tpl):
 return reduce(lambda x,y: x+flatten(y),tpl,()) if type(tpl) is tuple else (tpl,)
gravatar
Roberto
A safe variant of your solution, assuming the values are integers:

def flatten(tpl):
return tuple( map(int, repr(tpl).replace('(', ‘').replace(’)', ‘').split(’, ')) )


gravatar
Roberto
Posting again, sorry:

 def flatten(tpl):
        return tuple( map(int, repr(tpl).replace('(', '').replace(')', '').split(', ')) )
gravatar
jdm
oops, didn't see Will's, which is cleaner.
gravatar
Paddy3118
Since I've been glancing at the Y combinator

 >>> t= (1, (1, 2, (1, 2, 3), 3))
>>> _smash = lambda f: lambda t: sum(map(f, t), ()) if type(t) is tuple else (t,)
>>> Y = lambda f: (lambda x: x(x))(lambda y: f(lambda z: y(y)(z)))
>>> # From two non recursive functions:
>>> smash = Y(_smash)
>>> smash(t)
(1, 1, 2, 1, 2, 3, 3)
>>> 

Remember: Don't do this at home folks :-)

- Paddy.
gravatar
apoirier
One liner variation, working with any iterable:

flatten = lambda t: sum(map(flatten, t), ()) if hasattr(t, ‘__iter__’) else (t,)
gravatar
Eno

 from itertools import chain
flatten = lambda x :chain(*(flatten(v) if type(v) is tuple else (v,) for v in x))
t = (1, (1, 2, (1, 2, 3), 3))
print list(flatten(t))
I recommend a visit to http://www.challenge-you.com if you enjoy those kind of challenges.