Sequences: Lists and Tuples

Beginner-Friendly Teaching Edition — Python

What You Will Learn

  • Create and initialize lists and tuples.
  • Access elements of lists, tuples and strings.
  • Understand mutable and immutable sequences.
  • Use slicing and the del statement.
  • Pass lists and tuples to functions.
  • Sort and search sequences.
  • Use list methods such as append(), insert(), remove(), reverse() and copy().
  • Use lists as stacks.
  • Create lists with list comprehensions.
  • Use generator expressions, filter(), map() and reduce().
  • Process two-dimensional lists.
  • Use Seaborn and Matplotlib for static visualization of die-roll data.

Chapter Structure

5.1 Introduction5.2 Lists5.3 Tuples5.4 Unpacking5.5 Slicing5.6 del5.7 Passing Lists5.8 Sorting5.9 Searching5.10 List Methods5.11 Stacks5.12 Comprehensions5.13 Generators5.14 Filter/Map/Reduce5.15 Sequence Functions5.16 2D Lists5.17 Visualization5.18 Wrap-Up

5.1 Introduction

A sequence is an ordered collection of values. Python provides several sequence types, including strings, lists and tuples. This chapter focuses especially on lists and tuples and shows how to store, access, modify, search and process collections of data.

Real-world idea: A collection could be a shopping list, contacts list, books in a library, songs on a phone, players on a sports team or items in an investment portfolio. Python collections give us convenient ways to store and access related data.
Most important difference: Lists are mutable — they can change. Tuples are immutable — their sequence elements cannot be changed.

5.2 Lists

A list is an ordered collection written with square brackets []. Lists can contain values of the same type or different types, and they can grow or shrink while a program runs.

Creating a List

numbers = [-45, 6, 0, 72, 1543]
print(numbers)
[-45, 6, 0, 72, 1543]

Accessing List Elements

List indexing starts at 0.

numbers = [10, 20, 30, 40, 50]

print(numbers[0])
print(numbers[2])
print(numbers[-1])
10 30 50
IndexValue
010
120
230
340
450

Lists Are Mutable

numbers = [10, 20, 30]
numbers[1] = 99
print(numbers)
[10, 99, 30]

The chapter emphasizes that list elements can be modified and that lists can also grow and shrink.

Common error: Accessing an index that does not exist causes IndexError. Using a non-integer index causes TypeError.

Growing a List with +=

values = []

for number in range(1, 6):
    values += [number]

print(values)
[1, 2, 3, 4, 5]

5.3 Tuples

A tuple is another sequence type. It is similar to a list for accessing elements, but the tuple itself is immutable.

student = ('Amanda', 'Blue', 98)

print(student[0])
print(student[2])
Amanda 98

Tuple Immutability

numbers = (10, 20, 30)

# numbers[0] = 99   # TypeError
Important: A tuple cannot have one of its sequence elements replaced. Strings are also immutable. Lists are mutable.

Tuples Can Contain Mutable Objects

A tuple can contain a list. The tuple itself remains immutable, but the list inside it can still be modified.

student = ('Amanda', 'Blue', [98, 75, 87])

student[2][1] = 85

print(student)
('Amanda', 'Blue', [98, 85, 87])

The source demonstrates this distinction: the tuple structure is unchanged, but its nested list can be modified.

5.4 Unpacking Sequences

Unpacking means assigning the elements of a sequence to separate variables.

student = ('Amanda', [98, 85, 87])

first_name, grades = student

print(first_name)
print(grades)
Amanda [98, 85, 87]

You can unpack strings, lists, tuples and sequences produced by range(). The number of variables must match the number of elements, otherwise Python raises ValueError.

first, second = 'hi'
print(first, second)

a, b, c = range(10, 40, 10)
print(a, b, c)
h i 10 20 30
Memory trick: Sequence on the right → variables on the left, one value per variable.

5.5 Sequence Slicing

Slicing creates a new sequence containing a selected part of the original sequence. The basic form is:

sequence[start:stop]

The start index is included, but stop is excluded. The original list is not modified by an ordinary slice operation.

numbers = [2, 3, 5, 7, 11, 13, 17, 19]

print(numbers[2:6])
print(numbers[:6])
print(numbers[4:])
print(numbers[::2])
[5, 7, 11, 13] [2, 3, 5, 7, 11, 13] [11, 13, 17, 19] [2, 5, 11, 17]
SliceMeaning
numbers[2:6]Index 2 up to, but not including, 6
numbers[:6]Beginning through index 5
numbers[4:]Index 4 through the end
numbers[::2]Every second element
numbers[::-1]Reverse order

5.6 del Statement

The del statement can delete individual list elements, slices or even a variable.

numbers = list(range(10))

del numbers[-1]
print(numbers)

del numbers[0:2]
print(numbers)
[0, 1, 2, 3, 4, 5, 6, 7, 8] [2, 3, 4, 5, 6, 7, 8]

Deleting a Slice

numbers = list(range(10))

del numbers[::2]
print(numbers)
[1, 3, 5, 7, 9]

Deleting Everything in a List

numbers = [1, 2, 3]
del numbers[:]
print(numbers)
[]

The source also demonstrates that del numbers removes the variable itself; attempting to use it afterward causes NameError.

5.7 Passing Lists to Functions

Lists are mutable objects. When a list is passed to a function, the function receives a reference to that list. Therefore, modifying its elements inside the function changes the original list.

def modify_elements(items):
    for i in range(len(items)):
        items[i] *= 2

numbers = [10, 3, 7, 1, 9]

modify_elements(numbers)

print(numbers)
[20, 6, 14, 2, 18]
Compare with tuples: Passing a tuple to this function and trying to modify its elements causes TypeError, because tuples are immutable.

5.8 Sorting Lists

Sorting arranges elements into an order, usually ascending or descending.

sort()

numbers = [7, 2, 9, 1, 5]

numbers.sort()
print(numbers)

numbers.sort(reverse=True)
print(numbers)
[1, 2, 5, 7, 9] [9, 7, 5, 2, 1]

sorted()

numbers = [7, 2, 9, 1, 5]

ordered = sorted(numbers)

print(ordered)
print(numbers)
[1, 2, 5, 7, 9] [7, 2, 9, 1, 5]
Simple difference: list.sort() sorts the existing list. sorted() returns a new sorted result.

5.9 Searching Sequences

Python provides several simple ways to search a sequence.

Using in and not in

colors = ['red', 'green', 'blue']

print('green' in colors)
print('yellow' not in colors)
True True

index()

colors = ['red', 'green', 'blue', 'green']

print(colors.index('green'))
1

index() returns the position of the first matching element.

count()

colors = ['red', 'green', 'blue', 'green']

print(colors.count('green'))
2
Search idea: in answers "is it present?", index() answers "where is the first one?", and count() answers "how many are there?".

5.10 Other List Methods

MethodPurposeExample
append(x)Adds one item at the enditems.append(10)
insert(i, x)Inserts at a positionitems.insert(1, 10)
remove(x)Removes the first matching itemitems.remove(10)
reverse()Reverses the listitems.reverse()
copy()Creates a shallow copynew = items.copy()
items = [10, 20, 30]

items.append(40)
items.insert(1, 15)
items.remove(30)

print(items)

items.reverse()
print(items)
[10, 15, 20, 40] [40, 20, 15, 10]

5.11 Simulating Stacks with Lists

A stack follows LIFO: Last In, First Out. The most recently added item is the first one removed.

push → append()top → last elementpop → remove last
stack = []

stack.append('A')
stack.append('B')
stack.append('C')

print(stack.pop())
print(stack.pop())
print(stack)
C B ['A']
Real-world analogy: Think of a stack of plates. The last plate placed on top is the first plate you remove.

5.12 List Comprehensions

A list comprehension is a concise way to create a new list from an iterable.

Basic Form

squares = [x ** 2 for x in range(1, 6)]
print(squares)
[1, 4, 9, 16, 25]

With a Condition

numbers = [10, 3, 7, 1, 9, 4, 2, 8, 5, 6]

odd_squares = [x ** 2 for x in numbers if x % 2 != 0]

print(odd_squares)
[9, 49, 1, 81, 25]
Take each xCheck conditionCalculate expressionAdd to new list
Memory trick: List comprehension = "Do something for every item, optionally only when a condition is true."

5.13 Generator Expressions

Generator expressions look like list comprehensions, but use parentheses instead of square brackets.

numbers = [10, 3, 7, 1, 9]

squares_of_odds = (x ** 2 for x in numbers if x % 2 != 0)

for value in squares_of_odds:
    print(value, end=' ')
9 49 1 81

A generator expression produces values on demand instead of creating the complete list immediately. This can reduce memory use when the whole list is not needed at once.

List comprehension: creates a list now.
Generator expression: produces values as needed.

5.14 Filter, Map and Reduce

Functional-style programming provides ways to process collections.

filter()

filter() keeps values for which a function returns True.

def is_odd(x):
    return x % 2 != 0

numbers = [10, 3, 7, 1, 9, 4, 2, 8, 5, 6]

print(list(filter(is_odd, numbers)))
[3, 7, 1, 9, 5]

The chapter describes filter() as a higher-order-function capability: functions themselves can be passed as arguments.

map()

map() applies a function to each value.

numbers = [1, 2, 3, 4, 5]

squares = list(map(lambda x: x ** 2, numbers))

print(squares)
[1, 4, 9, 16, 25]

reduce()

A reduction processes many values into a single value, such as a total, product, minimum or maximum. Python's functools.reduce() can be used for custom reductions.

from functools import reduce

numbers = [1, 2, 3, 4]

total = reduce(lambda x, y: x + y, numbers)

print(total)
10
Easy memory:
filter → select
map → transform
reduce → combine into one result

5.15 Other Sequence Processing Functions

Python provides many built-in functions that work with sequences. Common examples include len(), sum(), min(), max(), any(), all() and enumerate().

enumerate()

enumerate() is especially useful when you need both an element and its index.

colors = ['red', 'green', 'blue']

for index, value in enumerate(colors):
    print(index, value)
0 red 1 green 2 blue

Sequence Reversal

numbers = [1, 2, 3, 4]

for value in reversed(numbers):
    print(value, end=' ')
4 3 2 1

Minimum and Maximum

numbers = [10, 3, 7, 1, 9]

print(min(numbers))
print(max(numbers))
print(sum(numbers))
1 10 30

5.16 Two-Dimensional Lists

A two-dimensional list is a list containing other lists. It is useful for representing data arranged in rows and columns, such as a table or matrix. The chapter processes these structures with nested for loops.

grades = [
    [88, 92, 95],
    [76, 85, 90],
    [91, 89, 94]
]

for row in grades:
    for value in row:
        print(value, end=' ')
    print()
88 92 95 76 85 90 91 89 94

Accessing a Specific Cell

print(grades[1][2])
90

Think of grades[row][column] as: first choose the row, then choose the item inside that row.

Visual idea: A 2D list is like a spreadsheet: the outer list represents rows and each inner list represents columns within a row.

5.17 Intro to Data Science: Simulation and Static Visualizations

This chapter moves from simply storing data to visualizing it. It uses Seaborn and Matplotlib to create static bar charts showing the results of a six-sided-die simulation. Seaborn is built on Matplotlib and simplifies many plotting operations.

Die-Roll Simulation

import random

rolls = [random.randrange(1, 7) for _ in range(600)]

frequencies = [
    rolls.count(face)
    for face in range(1, 7)
]

print(frequencies)

For a fair six-sided die, each face should appear approximately one-sixth of the time. With only 600 rolls, frequencies will not be exactly equal. As the number of rolls becomes much larger, the percentages become closer to the expected value of about 16.667% for each face.

Static Bar Chart

import matplotlib.pyplot as plt
import seaborn as sns

faces = list(range(1, 7))

sns.barplot(x=faces, y=frequencies)
plt.title('Die Frequencies')
plt.xlabel('Die Value')
plt.ylabel('Frequency')
plt.show()

The source chapter uses Seaborn and Matplotlib together to develop a static bar plot of die frequencies.

Law of Large Numbers

With 600 rolls, the bars can differ noticeably. With 60,000 rolls they become much closer in size, and with 6,000,000 rolls they appear nearly equal. This illustrates the law of large numbers: as the number of trials increases, observed percentages tend to get closer to expected probabilities.

Command-Line Arguments

The chapter also shows how a script can receive the number of die rolls from the command line through the Standard Library's sys module.

import random
import sys

rolls = [
    random.randrange(1, 7)
    for _ in range(int(sys.argv[1]))
]

For example, running a script with a value such as 600 makes that value available through sys.argv. The chapter notes that command-line arguments arrive as strings, so the roll count is converted with int().

5.17.1 Sample Graphs for 600, 60,000 and 6,000,000 Die Rolls

The chapter compares simulations at three scales. With more rolls, the frequencies and percentages of the six faces become increasingly similar. This is a practical demonstration of probability becoming more stable over many trials.

RollsWhat you should expect
600Noticeable differences between face frequencies.
60,000Frequencies become much closer.
6,000,000Bars appear nearly equal.

5.17.2 Visualizing Die-Roll Frequencies and Percentages

A good visualization should make the data easy to understand. The chapter's static visualization includes a title, x-axis label, y-axis label, bars for the die values and frequency/percentage information.

Generate rollsCount frequenciesCalculate percentagesBuild bar plotInterpret data
Data-science lesson: Raw numbers are useful, but a visualization can make patterns much easier to see.

5.18 Wrap-Up

This chapter develops lists and tuples in detail: creating them, accessing elements, modifying lists, slicing sequences, deleting elements, passing collections to functions, sorting and searching, and using list methods.

It also introduces stacks, list comprehensions, generator expressions, filtering and mapping, two-dimensional lists, and static data visualization.

Quick Concept Map

SequenceListTupleIndexSlicedelMutableImmutableSortSearchMethodsStackComprehensionGeneratorfiltermapreduce2D ListVisualization

Common Beginner Mistakes

  1. Forgetting that list indexing starts at 0.
  2. Using an index outside the valid range.
  3. Trying to modify a tuple element.
  4. Confusing numbers[2:6] with including index 6 — the stop index is excluded.
  5. Using del numbers when you only intended to clear the list.
  6. Forgetting that passing a mutable list to a function can allow the function to modify the original list.
  7. Confusing sort() with sorted().
  8. Calling index() for an item that is not present.
  9. Using a list comprehension when a generator would be more appropriate for very large data.
  10. Mixing up filter, map and reduce.
  11. Forgetting the second index when accessing a two-dimensional list.

Remember These Points

  • List = ordered and mutable.
  • Tuple = ordered and immutable.
  • Indexing starts at 0.
  • Negative indices count from the end.
  • Slicing creates a subset using start:stop:step.
  • del can remove elements, slices or a variable.
  • append() adds at the end; insert() adds at a position.
  • remove() removes a matching value.
  • sort() changes the list; sorted() returns a sorted result.
  • enumerate() gives index + value together.
  • Stack follows LIFO.
  • List comprehension creates lists concisely.
  • Generator expression produces values on demand.
  • filter selects; map transforms; reduce combines.
  • A 2D list is useful for row-and-column data.

Revision Questions

  1. What is a sequence?
  2. What is the main difference between a list and a tuple?
  3. Why are lists called mutable?
  4. What does index -1 mean?
  5. What happens when an index does not exist?
  6. What is sequence slicing?
  7. What does del do?
  8. Why can a function modify a list passed to it?
  9. What is the difference between sort() and sorted()?
  10. How do in, index() and count() differ?
  11. How can a list simulate a stack?
  12. What is a list comprehension?
  13. What is a generator expression?
  14. Explain filter(), map() and reduce().
  15. What is a two-dimensional list?
  16. What does the law of large numbers demonstrate in the die-roll simulation?

Practice Programs

  1. Create a list of five student marks and print the highest and lowest mark.
  2. Write a program that reverses a list using slicing.
  3. Create a tuple of three subjects and unpack it into three variables.
  4. Use slicing to extract the middle elements of a list.
  5. Write a function that doubles every element in a list.
  6. Sort a list in ascending and descending order.
  7. Build a stack using append() and pop().
  8. Create a list of squares using a list comprehension.
  9. Use a generator expression to produce the squares of odd numbers.
  10. Use filter() to select even numbers.
  11. Use map() to convert Celsius temperatures to Fahrenheit.
  12. Create a 3×3 two-dimensional list and print it with nested loops.
  13. Simulate 600 die rolls and display the frequency of each face.
  14. Create a static bar chart of the die frequencies with Seaborn/Matplotlib.

Teaching note: This HTML is a beginner-friendly transformative explanation based on the chapter's concepts and structure. It is designed for learning and revision rather than reproducing the book's original text.