Chapter 4: Working with Lists
|
Source: Python Crash Course, 3rd Edition by Eric Matthes |
In Chapter 3 you learned how to make a simple list, and you learned to work with the individual elements in a list. In this chapter you’ll learn how to loop through an entire list using just a few lines of code, regardless of how long the list is. Looping allows you to take the same action, or set of actions, with every item in a list. As a result, you’ll be able to work efficiently with lists of any length, including those with thousands or even millions of items.
Looping Through an Entire List
You’ll often want to run through all entries in a list, performing the
same task with each item. For example, in a game you might want to move
every element on the screen by the same amount. In a list of numbers,
you might want to perform the same statistical operation on every
element. Or perhaps you’ll want to display each headline from a list of
articles on a website. When you want to do the same action with every
item in a list, you can use Python’s for loop.
Say we have a list of magicians' names, and we want to print out each
name in the list. We could do this by retrieving each name from the list
individually, but this approach could cause several problems. For one,
it would be repetitive to do this with a long list of names. Also, we’d
have to change our code each time the list’s length changed. Using a
for loop avoids both of these issues by letting Python manage them
internally.
Let’s use a for loop to print out each name in a list of magicians:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(magician)
We begin by defining a list, just as we did in Chapter 3. Then we define
a for loop. This line tells Python to pull a name from the list
magicians, and associate it with the variable magician. Next, we
tell Python to print the name that’s just been assigned to magician.
Python then repeats these last two lines, once for each name in the
list. It might help to read this code as "For every magician in the
list of magicians, print the magician’s name." The output is a simple
printout of each name in the list:
alice
david
carolina
A Closer Look at Looping
Looping is important because it’s one of the most common ways a computer
automates repetitive tasks. For example, in a simple loop like we used
in magicians.py, Python initially reads the first line of the loop:
for magician in magicians:
This line tells Python to retrieve the first value from the list
magicians and associate it with the variable magician. This first
value is 'alice'. Python then reads the next line:
print(magician)
Python prints the current value of magician, which is still 'alice'.
Because the list contains more values, Python returns to the first line
of the loop, retrieves the next name 'david', and associates that
value with the variable magician. Python then executes the print()
call again. Python repeats the entire loop once more with the last value
in the list, 'carolina'. Because no more values are in the list,
Python moves on to the next line in the program. In this case nothing
comes after the for loop, so the program ends.
When you’re using loops for the first time, keep in mind that the set of steps is repeated once for each item in the list, no matter how many items are in the list. If you have a million items in your list, Python repeats these steps a million times β and usually very quickly.
Also keep in mind when writing your own for loops that you can choose
any name you want for the temporary variable that will be associated
with each value in the list. However, it’s helpful to choose a
meaningful name that represents a single item from the list. For
example, here’s a good way to start a for loop for a list of cats, a
list of dogs, and a general list of items:
for cat in cats:
for dog in dogs:
for item in list_of_items:
These naming conventions can help you follow the action being done on
each item within a for loop. Using singular and plural names can help
you identify whether a section of code is working with a single element
from the list or the entire list.
Doing More Work Within a for Loop
You can do just about anything with each item in a for loop. Let’s
build on the previous example by printing a message to each magician,
telling them that they performed a great trick:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(f"{magician.title()}, that was a great trick!")
The only difference in this code is where we compose a message to each
magician, starting with that magician’s name. The first time through the
loop the value of magician is 'alice', so Python starts the first
message with the name 'Alice'. The second time through, the message
will begin with 'David', and the third time through, the message will
begin with 'Carolina'.
The output shows a personalized message for each magician in the list:
Alice, that was a great trick!
David, that was a great trick!
Carolina, that was a great trick!
You can also write as many lines of code as you like in the for loop.
Every indented line following the line for magician in magicians is
considered inside the loop, and each indented line is executed once for
each value in the list. Therefore, you can do as much work as you like
with each value in the list.
Let’s add a second line to our message, telling each magician that we’re looking forward to their next trick:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(f"{magician.title()}, that was a great trick!")
print(f"I can't wait to see your next trick, {magician.title()}.\n")
Because we have indented both calls to print(), each line will be
executed once for every magician in the list. The newline "\n" in the
second print() call inserts a blank line after each pass through the
loop. This creates a set of messages that are neatly grouped for each
person in the list:
Alice, that was a great trick!
I can't wait to see your next trick, Alice.
David, that was a great trick!
I can't wait to see your next trick, David.
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
You can use as many lines as you like in your for loops. In practice,
you’ll often find it useful to do a number of different operations with
each item in a list when you use a for loop.
Doing Something After a for Loop
What happens once a for loop has finished executing? Usually, you’ll
want to summarize a block of output or move on to other work that your
program must accomplish.
Any lines of code after the for loop that are not indented are
executed once without repetition. Let’s write a thank you to the group
of magicians as a whole, thanking them for putting on an excellent show.
To display this group message after all of the individual messages have
been printed, we place the thank you message after the for loop,
without indentation:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(f"{magician.title()}, that was a great trick!")
print(f"I can't wait to see your next trick, {magician.title()}.\n")
print("Thank you, everyone. That was a great magic show!")
The first two calls to print() are repeated once for each magician in
the list, as you saw earlier. However, because the last line is not
indented, it’s printed only once:
Alice, that was a great trick!
I can't wait to see your next trick, Alice.
David, that was a great trick!
I can't wait to see your next trick, David.
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
Thank you, everyone. That was a great magic show!
When you’re processing data using a for loop, you’ll find that this is
a good way to summarize an operation that was performed on an entire
dataset. For example, you might use a for loop to initialize a game by
running through a list of characters and displaying each character on
the screen. You might then write some additional code after this loop
that displays a Play Now button after all the characters have been drawn
to the screen.
Avoiding Indentation Errors
Python uses indentation to determine how a line, or group of lines, is
related to the rest of the program. In the previous examples, the lines
that printed messages to individual magicians were part of the for
loop because they were indented. Python’s use of indentation makes code
very easy to read. Basically, it uses whitespace to force you to write
neatly formatted code with a clear visual structure. In longer Python
programs, you’ll notice blocks of code indented at a few different
levels. These indentation levels help you gain a general sense of the
overall program’s organization.
As you begin to write code that relies on proper indentation, you’ll need to watch for a few common indentation errors. Let’s examine some of the more common ones.
Forgetting to Indent
Always indent the line after the for statement in a loop. If you
forget, Python will remind you:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(magician) (1)
| 1 | The call to print() should be indented, but it’s not. |
When Python expects an indented block and doesn’t find one, it lets you know which line it had a problem with:
File "magicians.py", line 3
print(magician)
^
IndentationError: expected an indented block after 'for' statement on line 2
You can usually resolve this kind of indentation error by indenting the
line or lines immediately after the for statement.
Forgetting to Indent Additional Lines
Sometimes your loop will run without any errors but won’t produce the expected result. This can happen when you’re trying to do several tasks in a loop and you forget to indent some of its lines.
For example, this is what happens when we forget to indent the second line in the loop that tells each magician we’re looking forward to their next trick:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(f"{magician.title()}, that was a great trick!")
print(f"I can't wait to see your next trick, {magician.title()}.") (1)
| 1 | The second call to print() is supposed to be indented, but it’s not. |
Because Python finds at least one indented line after the for
statement, it doesn’t report an error. As a result, the first print()
call is executed once for each name in the list because it is indented.
The second print() call is not indented, so it is executed only once
after the loop has finished running. Because the final value associated
with magician is 'carolina', she is the only one who receives the
"looking forward to the next trick" message:
Alice, that was a great trick!
David, that was a great trick!
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
This is a logical error. The syntax is valid Python code, but the code does not produce the desired result because a problem occurs in its logic. If you expect to see a certain action repeated once for each item in a list and it’s executed only once, determine whether you need to simply indent a line or a group of lines.
Indenting Unnecessarily
If you accidentally indent a line that doesn’t need to be indented, Python informs you about the unexpected indent:
message = "Hello Python world!"
print(message)
We don’t need to indent the print() call, because it isn’t part of a
loop; hence, Python reports that error:
File "hello_world.py", line 2
print(message)
^
IndentationError: unexpected indent
You can avoid unexpected indentation errors by indenting only when you
have a specific reason to do so. In the programs you’re writing at this
point, the only lines you should indent are the actions you want to
repeat for each item in a for loop.
Indenting Unnecessarily After the Loop
If you accidentally indent code that should run after a loop has finished, that code will be repeated once for each item in the list. Sometimes this prompts Python to report an error, but often this will result in a logical error.
For example, let’s see what happens when we accidentally indent the line that thanked the magicians as a group for putting on a good show:
magicians = ['alice', 'david', 'carolina']
for magician in magicians:
print(f"{magician.title()}, that was a great trick!")
print(f"I can't wait to see your next trick, {magician.title()}.\n")
print("Thank you everyone, that was a great magic show!") (1)
| 1 | Because the last line is indented, it’s printed once for each person in the list. |
Alice, that was a great trick!
I can't wait to see your next trick, Alice.
Thank you everyone, that was a great magic show!
David, that was a great trick!
I can't wait to see your next trick, David.
Thank you everyone, that was a great magic show!
Carolina, that was a great trick!
I can't wait to see your next trick, Carolina.
Thank you everyone, that was a great magic show!
This is another logical error, similar to the one in Forgetting to Indent Additional Lines. Because Python doesn’t know what you’re trying to accomplish with your code, it will run all code that is written in valid syntax. If an action is repeated many times when it should be executed only once, you probably need to unindent the code for that action.
Forgetting the Colon
The colon at the end of a for statement tells Python to interpret the
next line as the start of a loop.
magicians = ['alice', 'david', 'carolina']
for magician in magicians (1)
print(magician)
| 1 | If you accidentally forget the colon, you’ll get a syntax error. |
File "magicians.py", line 2
for magician in magicians
^
SyntaxError: expected ':'
Python doesn’t know if you simply forgot the colon, or if you meant to
write additional code to set up a more complex loop. If the interpreter
can identify a possible fix it will suggest one, as it does here with
expected ':'. Some errors have easy, obvious fixes thanks to the
suggestions in Python’s tracebacks. Some errors are much harder to
resolve, even when the eventual fix only involves a single character.
Don’t feel bad when a small fix takes a long time to find β you are
absolutely not alone in this experience.
Try It Yourself
4-1. Pizzas: Think of at least three kinds of your favorite pizza.
Store these pizza names in a list, and then use a for loop to print
the name of each pizza. Modify your for loop to print a sentence using
the name of the pizza instead of printing just the name of the pizza.
For each pizza, you should have one line of output containing a simple
statement like I like pepperoni pizza. Add a line at the end of your
program, outside the for loop, that states how much you like pizza.
The output should consist of three or more lines about the kinds of
pizza you like and then an additional sentence, such as I really love
pizza!
4-2. Animals: Think of at least three different animals that have a
common characteristic. Store the names of these animals in a list, and
then use a for loop to print out the name of each animal. Modify your
program to print a statement about each animal, such as A dog would
make a great pet. Add a line at the end of your program, stating what
these animals have in common. You could print a sentence, such as Any
of these animals would make a great pet!
Making Numerical Lists
Many reasons exist to store a set of numbers. For example, you’ll need to keep track of the positions of each character in a game, and you might want to keep track of a player’s high scores as well. In data visualizations, you’ll almost always work with sets of numbers, such as temperatures, distances, population sizes, or latitude and longitude values, among other types of numerical sets.
Lists are ideal for storing sets of numbers, and Python provides a variety of tools to help you work efficiently with lists of numbers. Once you understand how to use these tools effectively, your code will work well even when your lists contain millions of items.
Using the range() Function
Python’s range() function makes it easy to generate a series of
numbers. For example, you can use the range() function to print a
series of numbers like this:
for value in range(1, 5):
print(value)
Although this code looks like it should print the numbers from 1 to 5, it doesn’t print the number 5:
1
2
3
4
In this example, range() prints only the numbers 1 through 4. This is
another result of the off-by-one behavior you’ll see often in
programming languages. The range() function causes Python to start
counting at the first value you give it, and it stops when it reaches
the second value you provide. Because it stops at that second value, the
output never contains the end value, which would have been 5 in this
case.
To print the numbers from 1 to 5, you would use range(1, 6):
for value in range(1, 6):
print(value)
This time the output starts at 1 and ends at 5:
1
2
3
4
5
If your output is different from what you expect when you’re using
range(), try adjusting your end value by 1.
You can also pass range() only one argument, and it will start the
sequence of numbers at 0. For example, range(6) would return the
numbers from 0 through 5.
Using range() to Make a List of Numbers
If you want to make a list of numbers, you can convert the results of
range() directly into a list using the list() function. When you
wrap list() around a call to the range() function, the output will
be a list of numbers.
In the example in the previous section, we simply printed out a series
of numbers. We can use list() to convert that same set of numbers into
a list:
numbers = list(range(1, 6))
print(numbers)
[1, 2, 3, 4, 5]
We can also use the range() function to tell Python to skip numbers in
a given range. If you pass a third argument to range(), Python uses
that value as a step size when generating numbers.
For example, here’s how to list the even numbers between 1 and 10:
even_numbers = list(range(2, 11, 2))
print(even_numbers)
In this example, the range() function starts with the value 2 and
then adds 2 to that value repeatedly until it reaches or passes the end
value, 11:
[2, 4, 6, 8, 10]
You can create almost any set of numbers you want to using the range()
function. For example, consider how you might make a list of the first
10 square numbers (that is, the square of each integer from 1 through
10). In Python, two asterisks (**) represent exponents. Here’s how you
might put the first 10 square numbers into a list:
squares = []
for value in range(1, 11):
square = value ** 2 (1)
squares.append(square) (2)
print(squares)
| 1 | The current value is raised to the second power and assigned to the
variable square. |
| 2 | Each new value of square is then appended to the list squares. |
Finally, when the loop has finished running, the list of squares is printed:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
To write this code more concisely, omit the temporary variable square
and append each new value directly to the list:
squares = []
for value in range(1, 11):
squares.append(value**2)
print(squares)
This line does the same work as the lines inside the for loop in the
previous listing. Each value in the loop is raised to the second power
and then immediately appended to the list of squares.
You can use either of these approaches when you’re making more complex lists. Sometimes using a temporary variable makes your code easier to read; other times it makes the code unnecessarily long. Focus first on writing code that you understand clearly, and does what you want it to do. Then look for more efficient approaches as you review your code.
Simple Statistics with a List of Numbers
A few Python functions are helpful when working with lists of numbers. For example, you can easily find the minimum, maximum, and sum of a list of numbers:
>>> digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> min(digits)
0
>>> max(digits)
9
>>> sum(digits)
45
|
The examples in this section use short lists of numbers that fit easily on the page. They would work just as well if your list contained a million or more numbers. |
List Comprehensions
The approach described earlier for generating the list squares
consisted of using three or four lines of code. A list comprehension
allows you to generate this same list in just one line of code. A list
comprehension combines the for loop and the creation of new elements
into one line, and automatically appends each new element. List
comprehensions are not always presented to beginners, but I’ve included
them here because you’ll most likely see them as soon as you start
looking at other people’s code.
The following example builds the same list of square numbers you saw earlier but uses a list comprehension:
squares = [value**2 for value in range(1, 11)]
print(squares)
To use this syntax, begin with a descriptive name for the list, such as
squares. Next, open a set of square brackets and define the expression
for the values you want to store in the new list. In this example the
expression is value2, which raises the value to the second power.
Then, write a for loop to generate the numbers you want to feed into
the expression, and close the square brackets. The for loop in this
example is for value in range(1, 11), which feeds the values 1 through
10 into the expression value2. Note that no colon is used at the end
of the for statement.
The result is the same list of square numbers you saw earlier:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
It takes practice to write your own list comprehensions, but you’ll find them worthwhile once you become comfortable creating ordinary lists. When you’re writing three or four lines of code to generate lists and it begins to feel repetitive, consider writing your own list comprehensions.
Try It Yourself
4-3. Counting to Twenty: Use a for loop to print the numbers from 1
to 20, inclusive.
4-4. One Million: Make a list of the numbers from one to one million,
and then use a for loop to print the numbers. (If the output is taking
too long, stop it by pressing Ctrl+C or by closing the output
window.)
4-5. Summing a Million: Make a list of the numbers from one to one
million, and then use min() and max() to make sure your list
actually starts at one and ends at one million. Also, use the sum()
function to see how quickly Python can add a million numbers.
4-6. Odd Numbers: Use the third argument of the range() function to
make a list of the odd numbers from 1 to 20. Use a for loop to print
each number.
4-7. Threes: Make a list of the multiples of 3, from 3 to 30. Use a
for loop to print the numbers in your list.
4-8. Cubes: A number raised to the third power is called a cube. For
example, the cube of 2 is written as 2**3 in Python. Make a list of
the first 10 cubes (that is, the cube of each integer from 1 through
10), and use a for loop to print out the value of each cube.
4-9. Cube Comprehension: Use a list comprehension to generate a list of the first 10 cubes.
Working with Part of a List
In Chapter 3 you learned how to access single elements in a list, and in this chapter you’ve been learning how to work through all the elements in a list. You can also work with a specific group of items in a list, called a slice in Python.
Slicing a List
To make a slice, you specify the index of the first and last elements
you want to work with. As with the range() function, Python stops one
item before the second index you specify. To output the first three
elements in a list, you would request indices 0 through 3, which would
return elements 0, 1, and 2.
The following example involves a list of players on a team:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[0:3])
This code prints a slice of the list. The output retains the structure of the list, and includes the first three players in the list:
['charles', 'martina', 'michael']
You can generate any subset of a list. For example, if you want the second, third, and fourth items in a list, you would start the slice at index 1 and end it at index 4:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[1:4])
This time the slice starts with 'martina' and ends with 'florence':
['martina', 'michael', 'florence']
If you omit the first index in a slice, Python automatically starts your slice at the beginning of the list:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[:4])
['charles', 'martina', 'michael', 'florence']
A similar syntax works if you want a slice that includes the end of a list. For example, if you want all items from the third item through the last item, you can start with index 2 and omit the second index:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[2:])
Python returns all items from the third item through the end of the list:
['michael', 'florence', 'eli']
This syntax allows you to output all of the elements from any point in
your list to the end, regardless of the length of the list. Recall that
a negative index returns an element a certain distance from the end of a
list; therefore, you can output any slice from the end of a list. For
example, if we want to output the last three players on the roster, we
can use the slice players[-3:]:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[-3:])
This prints the names of the last three players and will continue to work as the list of players changes in size.
|
You can include a third value in the brackets indicating a slice. If a third value is included, this tells Python how many items to skip between items in the specified range. |
Looping Through a Slice
You can use a slice in a for loop if you want to loop through a subset
of the elements in a list. In the next example, we loop through the
first three players and print their names as part of a simple roster:
players = ['charles', 'martina', 'michael', 'florence', 'eli']
print("Here are the first three players on my team:")
for player in players[:3]: (1)
print(player.title())
| 1 | Instead of looping through the entire list of players, Python loops through only the first three names. |
Here are the first three players on my team:
Charles
Martina
Michael
Slices are very useful in a number of situations. For instance, when you’re creating a game, you could add a player’s final score to a list every time that player finishes playing. You could then get a player’s top three scores by sorting the list in decreasing order and taking a slice that includes just the first three scores. When you’re working with data, you can use slices to process your data in chunks of a specific size. Or, when you’re building a web application, you could use slices to display information in a series of pages with an appropriate amount of information on each page.
Copying a List
Often, you’ll want to start with an existing list and make an entirely
new list based on the first one. To copy a list, you can make a slice
that includes the entire original list by omitting the first index and
the second index ([:]). This tells Python to make a slice that starts
at the first item and ends with the last item, producing a copy of the
entire list.
For example, imagine we have a list of our favorite foods and want to make a separate list of foods that a friend likes. This friend likes everything in our list so far, so we can create their list by copying ours:
my_foods = ['pizza', 'falafel', 'carrot cake']
friend_foods = my_foods[:] (1)
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
| 1 | Make a copy of my_foods by asking for a slice without specifying
any indices, and assign the copy to friend_foods. |
When we print each list, we see that they both contain the same foods:
My favorite foods are:
['pizza', 'falafel', 'carrot cake']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake']
To prove that we actually have two separate lists, we’ll add a new food to each list and show that each list keeps track of the appropriate person’s favorite foods:
my_foods = ['pizza', 'falafel', 'carrot cake']
friend_foods = my_foods[:] (1)
my_foods.append('cannoli') (2)
friend_foods.append('ice cream') (3)
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
| 1 | Copy the original items in my_foods to the new list friend_foods. |
| 2 | Add 'cannoli' to my_foods. |
| 3 | Add 'ice cream' to friend_foods. |
My favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake', 'ice cream']
The output shows that 'cannoli' now appears in our list of favorite
foods but 'ice cream' does not, and vice versa for our friend’s list.
If we had simply set friend_foods equal to my_foods, we would not
produce two separate lists. For example, here’s what happens when you
try to copy a list without using a slice:
my_foods = ['pizza', 'falafel', 'carrot cake']
# This doesn't work:
friend_foods = my_foods
my_foods.append('cannoli')
friend_foods.append('ice cream')
print("My favorite foods are:")
print(my_foods)
print("\nMy friend's favorite foods are:")
print(friend_foods)
Instead of assigning a copy of my_foods to friend_foods, we set
friend_foods equal to my_foods. This syntax actually tells Python to
associate the new variable friend_foods with the list that is already
associated with my_foods, so now both variables point to the same
list. As a result, when we add 'cannoli' to my_foods, it will also
appear in friend_foods. The output shows that both lists are now the
same, which is not what we wanted:
My favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']
My friend's favorite foods are:
['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']
|
Don’t worry about the details in this example for now. If you’re trying to work with a copy of a list and you see unexpected behavior, make sure you are copying the list using a slice, as we did in the first example. |
Try It Yourself
4-10. Slices: Using one of the programs you wrote in this chapter, add
several lines to the end of the program that do the following: print the
message The first three items in the list are:, then use a slice to
print the first three items from that program’s list. Print the message
Three items from the middle of the list are:, then use a slice to
print three items from the middle of the list. Print the message
The last three items in the list are:, then use a slice to print the
last three items in the list.
4-11. My Pizzas, Your Pizzas: Start with your program from Exercise
4-1. Make a copy of the list of pizzas, and call it friend_pizzas.
Then add a new pizza to the original list, add a different pizza to the
list friend_pizzas, and prove that you have two separate lists. Print
the message My favorite pizzas are:, and then use a for loop to
print the first list. Print the message My friend’s favorite pizzas
are:, and then use a for loop to print the second list. Make sure
each new pizza is stored in the appropriate list.
4-12. More Loops: All versions of foods.py in this section have
avoided using for loops when printing, to save space. Choose a version
of foods.py, and write two for loops to print each list of foods.
Tuples
Lists work well for storing collections of items that can change throughout the life of a program. However, sometimes you’ll want to create a list of items that cannot change. Tuples allow you to do just that. Python refers to values that cannot change as immutable, and an immutable list is called a tuple.
Defining a Tuple
A tuple looks just like a list, except you use parentheses instead of square brackets. Once you define a tuple, you can access individual elements by using each item’s index, just as you would for a list.
For example, if we have a rectangle that should always be a certain size, we can ensure that its size doesn’t change by putting the dimensions into a tuple:
dimensions = (200, 50)
print(dimensions[0])
print(dimensions[1])
We define the tuple dimensions using parentheses instead of square
brackets. Then we print each element in the tuple individually:
200
50
Let’s see what happens if we try to change one of the items in the tuple
dimensions:
dimensions = (200, 50)
dimensions[0] = 250
This code tries to change the value of the first dimension, but Python returns a type error:
Traceback (most recent call last):
File "dimensions.py", line 2, in <module>
dimensions[0] = 250
TypeError: 'tuple' object does not support item assignment
This is beneficial because we want Python to raise an error when a line of code tries to change the dimensions of the rectangle.
|
Tuples are technically defined by the presence of a comma; the parentheses make them look neater and more readable. If you want to define a tuple with one element, you need to include a trailing comma:
It doesn’t often make sense to build a tuple with one element, but this can happen when tuples are generated automatically. |
Looping Through All Values in a Tuple
You can loop over all the values in a tuple using a for loop, just as
you did with a list:
dimensions = (200, 50)
for dimension in dimensions:
print(dimension)
Python returns all the elements in the tuple:
200
50
Writing Over a Tuple
Although you can’t modify a tuple, you can assign a new value to a variable that represents a tuple. For example, if we wanted to change the dimensions of this rectangle, we could redefine the entire tuple:
dimensions = (200, 50)
print("Original dimensions:")
for dimension in dimensions:
print(dimension)
dimensions = (400, 100)
print("\nModified dimensions:")
for dimension in dimensions:
print(dimension)
The first four lines define the original tuple and print the initial
dimensions. We then associate a new tuple with the variable dimensions,
and print the new values. Python doesn’t raise any errors this time,
because reassigning a variable is valid:
Original dimensions:
200
50
Modified dimensions:
400
100
When compared with lists, tuples are simple data structures. Use them when you want to store a set of values that should not be changed throughout the life of a program.
Try It Yourself
4-13. Buffet: A buffet-style restaurant offers only five basic foods.
Think of five simple foods, and store them in a tuple. Use a for loop
to print each food the restaurant offers. Try to modify one of the items,
and make sure that Python rejects the change. The restaurant changes its
menu, replacing two of the items with different foods. Add a line that
rewrites the tuple, and then use a for loop to print each of the items
on the revised menu.
Styling Your Code
Now that you’re writing longer programs, it’s a good idea to learn how to style your code consistently. Take the time to make your code as easy as possible to read. Writing easy-to-read code helps you keep track of what your programs are doing and helps others understand your code as well.
Python programmers have agreed on a number of styling conventions to ensure that everyone’s code is structured in roughly the same way. Once you’ve learned to write clean Python code, you should be able to understand the overall structure of anyone else’s Python code, as long as they follow the same guidelines. If you’re hoping to become a professional programmer at some point, you should begin following these guidelines as soon as possible to develop good habits.
The Style Guide
When someone wants to make a change to the Python language, they write a Python Enhancement Proposal (PEP). One of the oldest PEPs is PEP 8, which instructs Python programmers on how to style their code. PEP 8 is fairly lengthy, but much of it relates to more complex coding structures than what you’ve seen so far.
The Python style guide was written with the understanding that code is read more often than it is written. You’ll write your code once and then start reading it as you begin debugging. When you add features to a program, you’ll spend more time reading your code. When you share your code with other programmers, they’ll read your code as well.
Given the choice between writing code that’s easier to write or code that’s easier to read, Python programmers will almost always encourage you to write code that’s easier to read. The following guidelines will help you write clear code from the start.
Indentation
PEP 8 recommends that you use four spaces per indentation level. Using four spaces improves readability while leaving room for multiple levels of indentation on each line.
In a word processing document, people often use tabs rather than spaces to indent. This works well for word processing documents, but the Python interpreter gets confused when tabs are mixed with spaces. Every text editor provides a setting that lets you use the Tab key but then converts each tab to a set number of spaces. You should definitely use your Tab key, but also make sure your editor is set to insert spaces rather than tabs into your document.
Mixing tabs and spaces in your file can cause problems that are very difficult to diagnose. If you think you have a mix of tabs and spaces, you can convert all tabs in a file to spaces in most editors.
Line Length
Many Python programmers recommend that each line should be less than 80 characters. Historically, this guideline developed because most computers could fit only 79 characters on a single line in a terminal window. Currently, people can fit much longer lines on their screens, but other reasons exist to adhere to the 79-character standard line length.
Professional programmers often have several files open on the same screen, and using the standard line length allows them to see entire lines in two or three files that are open side by side onscreen. PEP 8 also recommends that you limit all of your comments to 72 characters per line, because some of the tools that generate automatic documentation for larger projects add formatting characters at the beginning of each commented line.
The PEP 8 guidelines for line length are not set in stone, and some teams prefer a 99-character limit. Don’t worry too much about line length in your code as you’re learning, but be aware that people who are working collaboratively almost always follow the PEP 8 guidelines. Most editors allow you to set up a visual cue, usually a vertical line on your screen, that shows you where these limits are.
|
Appendix B shows you how to configure your text editor so it always inserts four spaces each time you press the Tab key and shows a vertical guideline to help you follow the 79-character limit. |
Blank Lines
To group parts of your program visually, use blank lines. You should use blank lines to organize your files, but don’t do so excessively. By following the examples provided in this book, you should strike the right balance. For example, if you have five lines of code that build a list and then another three lines that do something with that list, it’s appropriate to place a blank line between the two sections. However, you should not place three or four blank lines between the two sections.
Blank lines won’t affect how your code runs, but they will affect the readability of your code. The Python interpreter uses horizontal indentation to interpret the meaning of your code, but it disregards vertical spacing.
Other Style Guidelines
PEP 8 has many additional styling recommendations, but most of the guidelines refer to more complex programs than what you’re writing at this point. As you learn more complex Python structures, I’ll share the relevant parts of the PEP 8 guidelines.
Try It Yourself
4-14. PEP 8: Look through the original PEP 8 style guide at python.org/dev/peps/pep-0008. You won’t use much of it now, but it might be interesting to skim through it.
4-15. Code Review: Choose three of the programs you’ve written in this chapter and modify each one to comply with PEP 8. Use four spaces for each indentation level. Set your text editor to insert four spaces every time you press the Tab key, if you haven’t already done so (see Appendix B for instructions). Use less than 80 characters on each line, and set your editor to show a vertical guideline at the 80th character position. Don’t use blank lines excessively in your program files.
Summary
In this chapter, you learned how to work efficiently with the elements
in a list. You learned how to work through a list using a for loop,
how Python uses indentation to structure a program, and how to avoid
some common indentation errors. You learned to make simple numerical
lists, as well as a few operations you can perform on numerical lists.
You learned how to slice a list to work with a subset of items and how
to copy lists properly using a slice. You also learned about tuples,
which provide a degree of protection to a set of values that shouldn’t
change, and how to style your increasingly complex code to make it easy
to read.
In Chapter 5, you’ll learn to respond appropriately to different
conditions by using if statements. You’ll learn to string together
relatively complex sets of conditional tests to respond appropriately to
exactly the kind of situation or information you’re looking for. You’ll
also learn to use if statements while looping through a list to take
specific actions with selected elements from a list.
Applied Exercises: Ch 4 β Working with Lists
These exercises cover the same concepts as the chapter but use context
from real infrastructure, network security, and language learning work.
Save each as a separate .py file using lowercase and underscores, e.g.
ise_policy_loop.py.
Domus Digitalis / Homelab
D4-1. Node Loop: Create a list of all your Domus Digitalis node
hostnames (hypervisors, routers, k3s master, FreeIPA, Vault, ISE
homelab, NAS). Use a for loop to print a status message for each, such
as Checking health: kvm-01. After the loop, print a summary line like
Health check complete for all nodes.
D4-2. VLAN Range: Your VLAN IDs are not contiguous. Use range() and
a step to generate a list of VLAN IDs from 10 to 40, stepping by 10
(DATA, VOICE, GUEST, IoT). Print each one with a label like
VLAN 10 β DATA.
D4-3. Square Latency: Create a list of simulated round-trip latency
values in milliseconds β the squares of integers from 1 through 8 β using
a list comprehension. Print the list. Then use min(), max(), and
sum() to print the minimum, maximum, and total latency values.
D4-4. Stack Slice: Define a list of your full Domus Digitalis software stack in the order services start (VyOS, FreeIPA, Vault, Bind, k3s, Wazuh, Gitea, MinIO). Use slices to print the first three services (core networking layer), the middle two (PKI and DNS), and the last three (workload layer). Add a label before each slice.
D4-5. Immutable VLAN Core: Define a tuple called core_vlans
containing the IDs and names of your three most critical VLANs as
strings (e.g., 'VLAN 100 β INFRA'). Loop through the tuple and print
each entry. Then try to modify one entry and confirm Python rejects it.
Finally, redefine the entire tuple with an updated set of core VLANs and
loop through it again.
CHLA / ISE / Network Security
C4-1. Policy Loop: Create a list of ISE policy set names. Use a for
loop to print a message for each, such as
Evaluating policy set: 802.1X Wired. After the loop, print a line
like All policy sets evaluated.
C4-2. Syslog Source Range: Your Monad pipeline ingests syslog from
multiple sources numbered 1 through 6 (e.g., ISE nodes, FTD, Wazuh).
Use range(1, 7) and a for loop to print a message for each source
number like Connecting to syslog source 3.
C4-3. Log Volume Math: Use a list comprehension to generate a list
representing estimated daily log volumes (in thousands) for each of 7
days, calculated as day ** 2 * 5. Print the list, then print the
min(), max(), and sum().
C4-4. Pipeline Slice: Define a list representing the stages of your Monad β Sentinel pipeline in order (e.g., ingest, normalize, enrich, route, store-hot, store-cold, alert). Use slices to print the first two stages (ingestion), the middle three (processing), and the last two (output). Label each slice clearly.
C4-5. Immutable Node Tuple: Define a tuple called ise_nodes
containing the hostnames of your production ISE nodes. Loop through the
tuple to print each hostname. Try to add a node by index assignment and
confirm Python rejects it. Then redefine the tuple to simulate adding a
new node and loop through it again.
General Sysadmin / Linux
L4-1. Service Loop: Create a list of systemd services you manage on
Rocky Linux. Use a for loop to print Starting: <service> for each.
After the loop, print All services started.
L4-2. Port Range: Use range() to generate a list of common
privileged ports: 22, 80, 443, 8080, 8443 (use a hand-crafted list if
range() doesn’t fit cleanly). Loop through them and print
Checking port: <port>.
L4-3. Filesystem Math: Use a list comprehension to build a list of
simulated filesystem usage percentages: the square root approximation
value * 7 % 100 for values 1 through 10 (use integer math). Print
the list, then print min(), max(), and sum().
L4-4. Log Slice: Define a list of 10 recent syslog event descriptions in chronological order. Use slices to print the first three (oldest events), three from the middle, and the last three (most recent events). Label each group.
L4-5. Immutable Nameservers: Define a tuple called nameservers
containing your three DNS server IPs. Loop through the tuple and print
each one with a label like Nameserver: 10.100.0.1. Try to change one
value by index and confirm Python rejects it. Then redefine the tuple to
simulate a nameserver change.
Spanish / DELE C2
E4-1. Vocabulary Loop: Create a list of at least six Spanish words
from your current Don Quijote reading. Use a for loop to print each
word. After the loop, print Repaso completo.
E4-2. Chapter Range: Use range(1, 31) to represent the chapters of
Don Quijote you’ve read so far. Use a for loop to print
CapΓtulo <N> β leΓdo. for the first five chapters only, using a slice.
E4-3. Study Session Math: Use a list comprehension to generate a list
of cumulative study minutes for 7 days, calculated as day * 25. Print
the list. Use sum() to print total minutes studied, and max() to
print your longest session.
E4-4. Verb Conjugation Slice: Define a list of at least eight conjugated forms of a Spanish verb (e.g., all forms of ser or ir across tenses). Use slices to print the first three (present tense), three from the middle (past tense), and the last two (future/conditional). Label each group by tense.
E4-5. Immutable DELE Levels: Define a tuple called dele_levels
containing all DELE proficiency levels in order from A1 to C2. Loop
through the tuple and print each level. Try to reassign one level by
index and confirm Python rejects it. Then redefine the tuple to include
an unofficial C2+ level and loop through the updated tuple.