There are two ways to do this: slices, or a shared iterator.
The other answers show the slice method—which I think you could have gotten correct, if you just knew/remembered the step=3 to range
:
[lst[i:i+3] for i in range(0, len(lst), 3)]
The only major downside of this method is that it only works on a list or other sequence, not a general iterable. In your current code, this doesn't matter, because the thing you want to call it on is a list.
But it's worth knowing the alternative too:
i = iter(list)
zip(i, i, i)
iter just asks a sequence or other iterable for a single-pass iterator over its contents.
Then zip just advances them in lockstep, as usual.
Because all three of zip
's arguments are references to the exact same iterator, when it tries to advance one, it advances all of them. (This is why we can't just do zip(iter(i), iter(i), iter(i))
—then you'd have three separate iterators.)
But what if you want to group by 2, or 5? Writing separate functions for zip(i, i)
and zip(i, i, i, i, i)
and so on wouldn't be very nice.
If we had a sequence of n
references of the iterator, we could use *args
syntax, as described in the tutorial under Unpacking Argument Lists, to just call zip(*sequence)
.
And we can easily get such a sequence by using the * repetition operator: [i]*n
. (If you don't understand why that ends up with n
references to one iterator, instead of n
separate iterators, read the Python FAQ's entry on How do I create a multidimensional list?.)
And you can put that all together into a one-liner:
zip(*[iter(lst)]*n)
If there's a partial group left over, this will drop it, because that's what zip
does. So if you'd rather do something different in that case, you can just replace zip
with a different function—e.g., to pad the partial group with spaces, just:
itertools.zip_longest(*[iter(lst)]*3, fillvalue=' ')
The itertools recipes in the docs have a function caller grouper
which does this for you.