Easiest way is to try it on some data. For example, let's make a list:
>>> l = list("abcdefghijklmnopqrstuvwxyz")
>>> l
['a', 'b', 'c', 'd', 'e', 'f', ... 'x', 'y', 'z']
I shortened the list, but it's all the letters you expect. Now try it:
>>> list(chunks(l, 3))
[['a', 'b', 'c'],
 ['d', 'e', 'f'],
 ['g', 'h', 'i'],
 ['j', 'k', 'l'],
 ['m', 'n', 'o'],
 ['p', 'q', 'r'],
 ['s', 't', 'u'],
 ['v', 'w', 'x'],
 ['y', 'z']]
So what's happening here is that as i increments through the loop (skipping n elements at a time), the routine yields out the next n elements of the original list. This return strategy is called a "generator," and it's signaled by the yield statement. Yield is like return, but if the routine is called again, it will "keep going from where it last left off."
Another way to exercise this is in a loop:
>>> for c in chunks(l, 4):
>>>     print c
['a', 'b', 'c', 'd']
['e', 'f', 'g', 'h']
['i', 'j', 'k', 'l']
['m', 'n', 'o', 'p']
['q', 'r', 's', 't']
['u', 'v', 'w', 'x']
['y', 'z']
Every time through the loop, c becomes the next "chunk" of the original iterable. What's yielded from chunks to the loop is a slice of the original list l[i:i+n]. In this case, I asked for n=4 so you can see the difference.
As you learn how to use enumerate, chunks, and similar generators, you'll find them enormously helpful. See e.g. the itertools module.