Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Loop Details
#5
Loops can be combined together. For example, say we want to make a list that represents a deck of cards. We will abbreviate the card names, so the Five of Clubs is 5C and the Queen of Hearts is QH. Rather than type all of these abbreviations out by hand, we can have Python do it for us:

deck = []
for rank in 'A23456789TJQK':
    for suit in 'CDHS':
        deck.append(rank + suit)
That will give a list of all 52 card abbreviations. Let's step through this, so it is clear what is going one. First we have our empty deck, and then we have our for loop over the ranks of the cards. The first time through this loop (often called the "outer loop"), rank is set to 'A'. Remember that looping over a string gives us the characters of the string. Then we start the loop over suits (the "inner loop"), where suit is set to 'C'. Then we append our first card abbreviation to the deck, 'AC' for the Ace of Clubs.

Now the code indented under the inner loop is finished, so we go back to the top of the inner loop. Some new programmers expect the code to go back to the top of the outer loop. However, the code indented under the outer loop is not done until the inner loop is completely finished. So we go back to the inner loop over suits, and suit is set to 'D'. The rank variable has not been changed, so we append 'AD' to deck.

This continues through hearts and spades, until our deck list is ['AC', 'AD', 'AH', 'AS']. After 'AS' is appended to deck, the code indented under the inner loop is done. We go back to the top of the inner loop, and find there are no more suits to get. Then we go to just after the end of the inner loop. But that is the end of the code indented under the outer loop. So that triggers again and assigns '2' to rank. Now we go back to the top of the inner loop, which starts over again, and assigns 'C' to suit. Then we append our fifth card to the deck list, '2C'. The inner loop cycles through, appending all of the 2's to the deck, and then we get the next iteration of the outer loop and rank is set to '3'. This repeats until we get to the final card in the deck, 'KS'.

Let's say that we are in the inner loop, and we want to break out of the loop and stop building the deck. We might try this:

deck = []
for rank in 'A23456789TJQK':
    for suit in 'CDHS':
        if (rank, suit) == ('J', 'H'):
            break
        deck.append(rank + suit)
The above code will not stop building the deck when it gets to the Jack of Hearts. It will merely skip the Jack of Hearts and the Jack of Spades, but will include all of the queens and kings. What happens is that the break statement stops the inner loop, but we are still in the outer loop. So the outer loop goes to the next iteration, and assign 'Q' to rank, and then the inner loop starts up again. This is the same for the continue statement as well. A continue in the inner loop will only affect the inner loop, and won't skip to the next pass through the outer loop.

How might we deal with this situation? One method is to use a flag variable to do a second break:

deck = []
done = False
for rank in 'A23456789TJQK':
    for suit in 'CDHS':
        if (rank, suit) == ('J', 'H'):
            done = True
            break
        deck.append(rank + suit)
    if done:
        break
So now we are sending a message to the outer loop using the done variable, so that the outer loop also knows to also break. If you are familiar with try/except blocks, you can use an exception to stop both loops:

deck = []
try:
    for rank in 'A23456789TJQK':
        for suit in 'CDHS':
            if (rank, suit) == ('J', 'H'):
                raise RuntimeError('Loop finished.')
            deck.append(rank + suit)
except RuntimeError:
    pass
Another possibility is to use an else to continue past a break. While an else statement is attached to a particular loop, and is only used by that for, the code indented under the else statement is executed after the loop is finished. So breaks and continues in that block of code will affect the next loop out.

deck = []
for rank in 'A23456789TJQK':
    for suit in 'CDHS':
        if (rank, suit) == ('J', 'H'):
            break
        deck.append(rank + suit)
    else:
        continue
    break
So think through how this works. The first several times through the outer loop, the inner loop does not execute the break. Since no break occurs, the code indented under the else statement for the inner loop is executed. That is a continue statement, which takes us to the next iteration of the outer loop. Going to the top like that skips that final break statement.

Eventually we get to the Jack of Hearts, at which point we break out of the inner loop. Since we had a break statement, the else statement does not trigger, and the continue is not executed. That takes us to the final break statement, taking us out of the outer loop as well.

If that last example was rather confusing to you, that's why I don't like that way of breaking out of an inner loop. It's a bit simpler than using a flag variable, but it's also a bit harder to follow how it skips all over the place. Personally, I find using an exception to get out of multiple loops overkill, but it's really up to you.

If we go back to our original nested loop example, we see that it puts together every possible combination of rank and suit. If you are familiar with set theory or SQL joins, you will recognize this as the Cartesian product of the ranks and the suits. This is a very common thing you run into when programming. That's why Python has a product function to do this for us in the incredibly useful itertools package.

import itertools
deck = []
for rank, suit in itertools.product('A23456789TJQK', 'CDHS'):
    deck.append(rank + suit)
Now we generate the whole deck with just one loop, avoiding any issues with breaking out of multiple loops. It is still good to know how to break out of multiple loops. While the itertools package is awesome, and you should definitely check it out for other ways to simplify loops, it can't do everything.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply


Messages In This Thread
Loop Details - by ichabod801 - Sep-13-2019, 06:41 PM
RE: Loop Details - by ichabod801 - Sep-13-2019, 06:42 PM
RE: Loop Details - by ichabod801 - Sep-13-2019, 06:43 PM
RE: Loop Details - by ichabod801 - Sep-13-2019, 06:44 PM
RE: Loop Details - by ichabod801 - Sep-13-2019, 06:47 PM
RE: Loop Details - by ichabod801 - Sep-13-2019, 06:49 PM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020