11

In Lisp, you can have something like this:

(setf my-stuff '(1 2 "Foo" 34 42 "Ni" 12 14 "Blue"))
(format t "~{~d ~r ~s~%~}" my-stuff)

What would be the most Pythonic way to iterate over that same list? The first thing that comes to mind is:

mystuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]
for x in xrange(0, len(mystuff)-1, 3):
    print "%d %d %s" % tuple(mystuff[x:x+3])

But that just feels awkward to me. I'm sure there's a better way?


Well, unless someone later provides a better example, I think gnibbler's solution is the nicest\closest, though it may not be quite as apparent at first how it does what it does:

mystuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]
for x in zip(*[iter(mystuff)]*3):
    print "{0} {1} {2}".format(*x)
Wayne Werner
  • 49,299
  • 29
  • 200
  • 290

6 Answers6

13
mystuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]
for x in zip(*[iter(mystuff)]*3):
    print "%d %d %s"%x

Or using .format

mystuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]
for x in zip(*[iter(mystuff)]*3):
    print "{0} {1} {2}".format(*x)

If the format string is not hardcoded, you can parse it to work out how many terms per line

from string import Formatter
num_terms = sum(1 for x in Formatter().parse("{0} {1} {2}"))

Putting it all together gives

mystuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]
fmt = "{0} {1} {2}"
num_terms = sum(1 for x in Formatter().parse(fmt))
for x in zip(*[iter(mystuff)]*num_terms):
    print fmt.format(*x)
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • I *really* like this solution, and I was really confused at first but after hacking at the code that is really elegant. – Wayne Werner Jul 01 '10 at 21:20
6

I think join is the most similar feature in Python:

(format t "~{~D, ~}" foo)

print(foo.join(", "))

It's a little worse when you have multiple items inside, as you see, though if you have a group-by function (which is really useful anyway!), I think you can make it work without too much trouble. Something like:

mystuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]
print(["%d %d %s" % x for x in group(mystuff, 3)].join("\n"))
Ken
  • 9,797
  • 3
  • 16
  • 14
  • Yes, with the addition of a specialized generator function you could make it look really nice. – Wayne Werner Jul 01 '10 at 21:13
  • Yeah, I guess it depends on how much you want to cheat. You could port `FORMAT` to Python and say `FORMAT(True, "~{~d ~r ~s~%~}", my_stuff)`, if you were really bored some weekend. :-) – Ken Jul 01 '10 at 21:18
  • I've been tempted! Of course I also wish that the `~r` were part of Python libs, too... There's already a module on sourceforge: http://sourceforge.net/projects/pynum2word/ – Wayne Werner Jul 01 '10 at 21:27
4

For starters, I'd use the newer string formatting methods in 2.6+

print "{0} {1} {2}".format(*mystuff[x:x+3])
Josh Wright
  • 2,495
  • 16
  • 20
3

I'd say the most Pythonic would be to make the list deeper:

mystuff = [(1, 2, "Foo"), (34, 42, "Ni"), (12, 14, "Blue")]
for triplet in mystuff:
    print "%d %d %s" % triplet
Walter Mundt
  • 24,753
  • 5
  • 53
  • 61
  • that's basically what gnibblers solution creates: `[x for x in zip(*[iter(mystuff)]*3)]` = `[(1, 2, 'foo'), (34, 42, 'ni'), (12, 14, 'blue')]` Pretty slick, neh? – Wayne Werner Jul 02 '10 at 12:28
  • I know that, but I think it's more Pythonic just to have the list in that form to begin with. That was the original question, right? – Walter Mundt Jul 02 '10 at 13:12
  • Well, I was a little ambiguous, so I fixed the wording. I really was more interested in the iterating/formatting part - consuming a certain # of list values per iteration. But in terms of the whole "program", your solution would be more pythonic – Wayne Werner Jul 02 '10 at 14:05
2
stuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]

it = iter(stuff)
itn = it.next

print '\n'.join("%d %d %s" % (el,itn(),itn())
                for el in it)

Very understandable, I think

eyquem
  • 26,771
  • 7
  • 38
  • 46
0

A two liner based on Wright:

mystuff = [1, 2, "Foo", 34, 42, "Ni", 12, 14, "Blue"]
print '\n'.join("{0},{1},{2}".format(*mystuff[x:x+3]) for x in xrange(0, len(mystuff)-1, 3))
ksed
  • 335
  • 1
  • 4
  • 13