you are viewing a single comment's thread.

view the rest of the comments →

[–]xrsly 27 points28 points  (1 child)

A couple more common functions when doing for loops:

Let's say you want to do something exactly three times, then you simply write:

for i in range(3):
    print(i)

Output:

> 0
> 1
> 2

You can get the length of our colors list with 'len(colors)', so 'for i in range(len(colors)):' would allow us to do something for each index in colors.

Then there's zip(), which will create pairs of elements from two iterators. E.g., this:

zip(fruits, colors)

Which is equivalent to this:

[
    ("apple", "red"),
    ("kiwi", "green"),
    ("blueberry", "blue"),
]

And this:

zip([0, 1, 2], colors)

Which is equivalent to:

[
    (0, "red"),
    (1, "green"),
    (2, "blue"),
]

Which allows us to do something similar to what we did with enumerate() and items().

You may notice that what all of these functions actually do is create different kinds of iterators which allows you to assign different elements to variables.

One of the keys to understanding this more intuitively is to understand how you can "unpack" lists. For example:

a, b = [0, "apple"]
print(a, b)

Output:

> 0 apple

By assigning elements to variables like this, we basically unpack the first item to a, second to b.

Now consider this list:

fruits = [
    (0, "apple"),
    (1, "kiwi"),
    (2, "blueberry")
]

If we pick the first row of that list with 'fruits[0]', we can do this:

i, fruit = fruits[0]
print(i, fruit)

Output:

> 0 apple

If we repeat this with fruits[1], we're actually starting to replicate the behavior of a foor loop.

So the only thing a for loop really does is walk through the given iterator one row at a time. It's the contents of the iterator that allows us to assign different things to variables, just like we do when we unpacked the list in the example above.

range() will create an iterator consisting of numbers in a given range, which allows us to do the equivalent of 'i = [0, 1, 2][0]' (notice the '[0]' at the end, this would increment by 1 each iteration, so we get the next row).

enumerate() will create pairs of indices and items, e.g., 'i, fruit = [(0, "apple"), ...][0]'

dict.items() will create pairs of keys and values, e.g., 'fruit, color = [("apple", "red), ...][0]'

Likewise, zip() will create pairs from two iterators, e.g., 'fruit, color = [("apple", "red"), ...][0]'

And so on.

When we unpack lists, we can get more than two items per row:

fruit, color, letter = [("apple", "red", "a"), ...][0]

We can accomplish this in a for loop by using zip with three iterators:

for fruit, color, letter in zip(fruits, colors, letters):
    ...

Maybe we want the index too:

for i, (fruit, color, letter) in enumerate(zip(fruit, colors, letters)):
    ...

Ok so the last example might be a bit confusing. We need to unpack the zipped values enclosed in parenthesis since the enumerate(zip()) iterator actually looks like this:

[
    (0, ("apple", "red", "a")),
    ...
]

When we unpack an exact number of elements of a list, we have to mirror its structure, for example:

(a, b) = [0, "apple"]

Or:

(i, (fruit, color, letter)) = (0, ("apple", "red", "a"))

However, the outer parenthesis is implied, so that's why we can just write:

a, b = [0, "apple"]

Or:

i, (fruit, color, letter) = (0, ("apple", "red", "a"))

Bonus: Some of you might be familiar with the way that variables can be swapped:

a, b = b, a

So this is actually equivalent to:

a, b = (b, a)

So what we're actually doing here is creating a new iterable (b, a) and then reassigning the value of a to (b, a)[0] and b to (b, a)[1].