Chapter 5: Lists and Dictionaries

Fundamentals of Python: First Programs

Mark Ira Galang

Objectives (1 of 2)

  • 5.1 Construct lists and access items in those lists
  • 5.2 Use methods to manipulate lists
  • 5.3 Perform traversals of lists to process items in the lists
  • 5.4 Define simple functions that expect parameters and return values

Objectives (2 of 2)

  • 5.5 Construct dictionaries and access entries in those dictionaries
  • 5.6 Use methods to manipulate dictionaries
  • 5.7 Determine whether a list or a dictionary is an appropriate data structure for a given application

Introduction

  • A list allows the programmer to manipulate a sequence of data values of any types
  • A dictionary organizes data values by association with other data values rather than by sequential position
  • Lists and dictionaries provide powerful ways to organize data in useful and interesting applications

Lists

  • List: Sequence of data values (items or elements)
  • Some examples:
    • Shopping list for the grocery store
    • To-do list
    • Roster for an athletic team
    • Guest list for a wedding
    • Recipe, which is a list of instructions
    • Text document, which is a list of lines
    • Names in a phone book
  • Each item in a list has a unique index that specifies its position (from 0 to length – 1)

List Literals and Basic Operators (1 of 3)

Some examples:

fruits = ['apples', 'oranges', 'cherries']
coordinates = [[5, 9], [541, 78]]
# Print out the list of fruits
print(fruits)
['apples', 'oranges', 'cherries']
# print out the coordiaties
print(coordinates)
[[5, 9], [541, 78]]

Expressions are also included:

import math
x = 2
numbers = [x, math.sqrt(x)]
print(numbers)
[2, 1.4142135623730951]

List Literals and Basic Operators (2 of 3)

  • Lists of integers can be build from the range function:
first = [1, 2, 3, 4]
second = list(range(1, 5))
print(first)
[1, 2, 3, 4]
print(second)
[1, 2, 3, 4]
  • The list function can build a list from any iterable sequence of elements:
third = list("I'm a baddie baby, that's how my mommy made me")
print(third)
['I', "'", 'm', ' ', 'a', ' ', 'b', 'a', 'd', 'd', 'i', 'e', ' ', 'b', 'a', 'b', 'y', ',', ' ', 't', 'h', 'a', 't', "'", 's', ' ', 'h', 'o', 'w', ' ', 'm', 'y', ' ', 'm', 'o', 'm', 'm', 'y', ' ', 'm', 'a', 'd', 'e', ' ', 'm', 'e']

List Literals and Basic Operators (3 of 3)

  • len, [], +, and == work on lists as expected:
print(first[1])
print(len(first))
print(first[2:4])
2
4
[3, 4]
  • Concatenation (+) and equality (==) also work as expected for lists:
print(first + [5, 6])
print(first == second)
[1, 2, 3, 4, 5, 6]
True
  • the in command detects the presense of an element
print (3 in [1, 2, 3])
print (0 in [1, 2, 3])
True
False

Replacing an Element in a List (1 of 2)

  • A list is mutable
    • Elements can be inserted, removed, or replaced
    • The list itself maintains its identity, but its state—its length and its contents—can change
  • Subscript operator is used to replace an element:
example = [1, 2, 3, 4]
print(example)
example[2] = 0
print(example)
[1, 2, 3, 4]
[1, 2, 0, 4]
  • Subscript is used to reference the target of the assignment, which is not the list but an element’s position within it

Replacing an Element in a List (2 of 2)

  • The first session shows how to replace each number in a list with its square:
numbers = [2, 3, 4, 5]
print(f"Before for loop: {numbers}")
for index in range(len(numbers)):
  numbers[index] = numbers[index] ** 2
print(f"After for loop: {numbers}")
Before for loop: [2, 3, 4, 5]
After for loop: [4, 9, 16, 25]
  • Next session uses the string method split to extract a list of words:
sentence = "Anyone know if the student center is open twenty four seven?"
words = sentence.split()
print(f"Before for loop: {words}")
for index in range(len(words)):
  words[index] = words[index].upper()
print(f"After for loop: {words}")
Before for loop: ['Anyone', 'know', 'if', 'the', 'student', 'center', 'is', 'open', 'twenty', 'four', 'seven?']
After for loop: ['ANYONE', 'KNOW', 'IF', 'THE', 'STUDENT', 'CENTER', 'IS', 'OPEN', 'TWENTY', 'FOUR', 'SEVEN?']

List Methods for Inserting and Removing Elements (1 of 4)

Suppose you have a list L with another list aList

List Methods What it Does
L.append(element) Adds element to the end of L
L.extend(aList) Adds the elements of aList to the end of L
L.insert(index, element) Inserts element at index if index is less than the length of L. Otherwise, inserts element at the end of L.
L.pop() Removes and returns the element at the end of L.
L.pop(index) Removes and returns the element at index

List Methods for Inserting and Removing Elements (2 of 4)

  • The method insert expects an integer index and the new element as arguments
example = [1, 2]
print(f"Before using .insert() {example}")
example.insert(1, 10)
print(f"After using .insert() {example}")
example.insert(10, 25)
print(f"After using .insert() again {example}")
Before using .insert() [1, 2]
After using .insert() [1, 10, 2]
After using .insert() again [1, 10, 2, 25]

List Methods for Inserting and Removing Elements (3 of 4)

  • The method append expects just the new element as an argument and adds the new element to the end of the list
  • The method extend performs a similar operation, but adds the elements of its list argument to the end of the list
example = [1, 2]
print(f"Before appending {example}")
example.append(3)
print(f"After appending {example}")
example.extend([11, 12, 13])
print(f"After Extending {example}")
Before appending [1, 2]
After appending [1, 2, 3]
After Extending [1, 2, 3, 11, 12, 13]

List Methods for Inserting and Removing Elements (4 of 4)

  • The method pop is used to remove an element at a given position
example = [1, 2, 10, 11, 12, 13]
print(f"Element removed: {example.pop()}") # Remove the last element
print(example)
print(f"Element removed: {example.pop(0)}") # Remove the first element
print(example)
Element removed: 13
[1, 2, 10, 11, 12]
Element removed: 1
[2, 10, 11, 12]

Searching a List

  • in determines an element’s presence or absence, but does not return position of element (if found)
  • Use method index to locate an element’s position in a list
    • Raises an error when the target element is not found
aList = [34, 45, 67]
target = 45
if target in aList:
    print(aList.index(target))
else:
    print(-1)
1

Sorting a list

  • A list’s elements are always ordered by position, but you can impose a natural ordering on them
    • For example, in alphabetical order
  • When the elements can be related by comparing them <, >, and ==, they can be sorted
    • The method sort mutates a list by arranging its elements in ascending order
example = [4, 2, 10, 8]
print(f"Before sorting: {example}")
example.sort()
print(f"After sorting: {example}")
Before sorting: [4, 2, 10, 8]
After sorting: [2, 4, 8, 10]
  • We will learn about sorting methods in Ch11

Aliasing and Side Effects (1 of 2)

Mutable property of lists leads to interesting phenomena:

first = [10, 20, 30]
second = first

print(first)
print(second)

first[1] = 99
print(first)
print(second)
[10, 20, 30]
[10, 20, 30]
[10, 99, 30]
[10, 99, 30]
  • First and second are aliases
    • They refer to the exact same list object

Aliasing and Side Effects (2 of 2)

To prevent aliasing, create a new object and copy contents of original:

third = []
for element in first:
  third.append(element)
print(first)
print(third)

# change the element
first[1] = 100
print(first)
print(third)
[10, 99, 30]
[10, 99, 30]
[10, 100, 30]
[10, 99, 30]

.copy() also works

fourth = first.copy()
print(first)
print(fourth)

first[1] = 67
print(first)
print(fourth)
[10, 100, 30]
[10, 100, 30]
[10, 67, 30]
[10, 100, 30]

Tuples

  • A tuple resembles a list, but is immutable
  • Indicate by enclosing its elements in ()
fruits = ("apple", "banana")
print(fruits)

meats = ("fish", "poultry")
food = meats + fruits
print(food)

veggies = ["celery", "beans"]
print(tuple(veggies))
('apple', 'banana')
('fish', 'poultry', 'apple', 'banana')
('celery', 'beans')

Defining Simple Functions

  • Defining our own functions allows us to organize our code in existing scripts more effectively
def square(x):
    """Return the square of x."""
    return x * x

# Test the function
print(square(5))
print(square(7))
25
49

Function Syntax

  • A function definition consists of header and body
  • Syntax:
def <function name>(<parameter-1>, ..., <parameter-n>):
    <body>
def greet(name):
    """Prints a greeting."""
    print(f"Hello, {name}!")

greet("Alice")
greet("Bob")
Hello, Alice!
Hello, Bob!

Boolean Functions

  • A Boolean function usually tests its argument for the presence or absence of some property
  • Returns True if property is present; False otherwise
def odd(x):
    """Returns True if x is odd or False otherwise."""
    if x % 2 == 1:
        return True
    else:
        return False

print(odd(5))
print(odd(6))
True
False

Main Function Pattern

  • main serves as the entry point for a script
def square(x):
    """Returns the square of x."""
    return x * x
  
print(square(2))
print(square(10))
4
100

Dictionary Basics

  • A dictionary organizes information by association, not position
  • In Python, a dictionary associates a set of keys with data values
# Phone book example
phone_book = {'Savannah': '476-3321', 'Nathaniel': '351-7743'}
print(phone_book)

# Personal information
person = {'Name': 'Molly', 'Age': 18}
print(person)

# Empty dictionary
empty_dict = {}
print(empty_dict)
{'Savannah': '476-3321', 'Nathaniel': '351-7743'}
{'Name': 'Molly', 'Age': 18}
{}

Adding and Replacing Dictionary Entries

  • Add a new key/value pair using []
  • Use [] also to replace a value at an existing key
info = {}
info["name"] = "Sandy"
info["occupation"] = "hacker"
print(info)

# Replace a value
info["occupation"] = "manager"
print(info)
{'name': 'Sandy', 'occupation': 'hacker'}
{'name': 'Sandy', 'occupation': 'manager'}

Accessing Dictionary Values

  • Use [] to obtain the value associated with a key
  • If key is not present, an error is raised
info = {'name': 'Sandy', 'occupation': 'manager'}

print(info["name"])

# Safe access with get
print(info.get("job", "Not found"))
print(info.get("name", "Not found"))
Sandy
Not found
Sandy

Removing Dictionary Entries

  • To delete an entry, remove its key using the method pop
info = {'name': 'Sandy', 'occupation': 'manager', 'age': 25}
print(f"Before pop: {info}")

# pop returns the value
value = info.pop("occupation")
print(f"Popped value: {value}")
print(f"After pop: {info}")

# pop with default value
result = info.pop("job", None)
print(f"Result: {result}")
Before pop: {'name': 'Sandy', 'occupation': 'manager', 'age': 25}
Popped value: manager
After pop: {'name': 'Sandy', 'age': 25}
Result: None

Traversing Dictionaries (1 of 2)

  • To print all of the keys and their values:
info = {'name': 'Sandy', 'occupation': 'manager', 'age': 25}

for key in info:
    print(key, info[key])
name Sandy
occupation manager
age 25
  • Using the items() method:
grades = {90: 'A', 80: 'B', 70: 'C'}
for key, value in grades.items():
    print(key, value)
90 A
80 B
70 C

Traversing Dictionaries (2 of 2)

  • You can sort the keys first to print in alphabetical order:
info = {'name': 'Sandy', 'occupation': 'manager', 'age': 25}

theKeys = list(info.keys())
theKeys.sort()
for key in theKeys:
    print(key, info[key])
age 25
name Sandy
occupation manager

Dictionary Methods Summary

Operation What It Does
len(d) Returns the number of entries
d[key] Insert/replace/access value
d.get(key [, default]) Returns value if key exists, else default
d.pop(key [, default]) Removes key and returns value, else default
list(d.keys()) Returns a list of keys
list(d.values()) Returns a list of values
list(d.items()) Returns a list of (key, value) tuples
d.clear() Removes all entries

Application: Hexadecimal to Binary

hexToBinaryTable = {
    '0': '0000', '1': '0001', '2': '0010', '3': '0011',
    '4': '0100', '5': '0101', '6': '0110', '7': '0111',
    '8': '1000', '9': '1001', 'A': '1010', 'B': '1011',
    'C': '1100', 'D': '1101', 'E': '1110', 'F': '1111'
}

def convert(number, table):
    """Builds and returns the base two representation of number."""
    binary = ""
    for digit in number:
        binary = binary + table[digit]
    return binary

print(convert("35A", hexToBinaryTable))
print(convert("F", hexToBinaryTable))
001101011010
1111

Application: Finding the Mode (1 of 2)

  • The mode of a list of values is the value that occurs most frequently
# Sample data (in practice, this would come from a file)
words = ["apple", "banana", "apple", "orange", "banana", "apple", "grape"]

# Build frequency dictionary
theDictionary = {}
for word in words:
    word = word.upper()
    number = theDictionary.get(word, None)
    if number == None:
        theDictionary[word] = 1
    else:
        theDictionary[word] = number + 1

print("Frequency dictionary:", theDictionary)
Frequency dictionary: {'APPLE': 3, 'BANANA': 2, 'ORANGE': 1, 'GRAPE': 1}

Application: Finding the Mode (2 of 2)

# Find the mode by obtaining the maximum value
theMaximum = max(theDictionary.values())
print(f"Maximum frequency: {theMaximum}")

for key in theDictionary:
    if theDictionary[key] == theMaximum:
        print(f"The mode is: {key}")
        break
Maximum frequency: 3
The mode is: APPLE

Choosing Lists vs Dictionaries

Scenario Best Choice Why?
Ordered sequence of items List Position matters
Need to look up by key Dictionary Fast key-based lookup
Shopping list List Simple sequence
Phone book Dictionary Look up by name
Student grades Dictionary Look up by student ID
Steps in a recipe List Order matters
Word frequency counter Dictionary Word → count mapping

Two-Dimensional Lists

  • A 2D list is a list where each element is itself a list
  • Also called a nested list or matrix
  • Use two indices: list_name[row][column] (both start at 0)
# Creating a 2D list (3 rows, 3 columns)
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

print("Matrix:")
for row in matrix:
    print(row)
Matrix:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

Accessing and Modifying Elements

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

# Access elements
print(f"First row: {matrix[0]}")
print(f"First element: {matrix[0][0]}")
print(f"Middle element: {matrix[1][1]}")

# Modify elements
matrix[1][1] = 99
print(f"\nAfter modification:")
for row in matrix:
    print(row)
First row: [1, 2, 3]
First element: 1
Middle element: 5

After modification:
[1, 2, 3]
[4, 99, 6]
[7, 8, 9]

Traversing a 2D List (Nested Loops)

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

print("All elements with indices:")
for row in range(len(matrix)):          # For each row
    for col in range(len(matrix[row])): # For each column
        print(f"matrix[{row}][{col}] = {matrix[row][col]}")

print("\nDisplay as grid:")
for row in matrix:
    for val in row:
        print(f"{val:3}", end="")
    print()  # New line after each row
All elements with indices:
matrix[0][0] = 1
matrix[0][1] = 2
matrix[0][2] = 3
matrix[1][0] = 4
matrix[1][1] = 5
matrix[1][2] = 6
matrix[2][0] = 7
matrix[2][1] = 8
matrix[2][2] = 9

Display as grid:
  1  2  3
  4  5  6
  7  8  9

Creating 2D Lists with Comprehension

# 3x3 matrix filled with zeros
zeros = [[0 for col in range(3)] for row in range(3)]
print("Zeros matrix:")
for row in zeros:
    print(row)

# 4x4 identity matrix
identity = [[1 if col == row else 0 for col in range(4)] for row in range(4)]
print("\nIdentity matrix:")
for row in identity:
    print(row)
Zeros matrix:
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]

Identity matrix:
[1, 0, 0, 0]
[0, 1, 0, 0]
[0, 0, 1, 0]
[0, 0, 0, 1]

Practical Example: Student Grades

# Rows = students, Columns = assignments
grades = [
    [95, 87, 92],  # Student 0
    [78, 85, 91],  # Student 1
    [88, 92, 89],  # Student 2
]

# Calculate average for each student
for i in range(len(grades)):
    total = sum(grades[i])
    average = total / len(grades[i])
    print(f"Student {i}: average = {average:.1f}")

# Calculate average for each assignment
for j in range(len(grades[0])):
    total = 0
    for i in range(len(grades)):
        total += grades[i][j]
    average = total / len(grades)
    print(f"Assignment {j}: average = {average:.1f}")
Student 0: average = 91.3
Student 1: average = 84.7
Student 2: average = 89.7
Assignment 0: average = 87.0
Assignment 1: average = 88.0
Assignment 2: average = 90.7

Common Pitfall: Copying vs Referencing

# WRONG way - creates references to the same row
wrong = [[0] * 3] * 3
print("Wrong initialization:")
for row in wrong:
    print(row)

# Modifying one row affects all rows!
wrong[0][0] = 99
print("\nAfter modification:")
for row in wrong:
    print(row)

# RIGHT way - create independent rows
correct = [[0 for col in range(3)] for row in range(3)]
print("\nCorrect initialization:")
correct[0][0] = 99
for row in correct:
    print(row)
Wrong initialization:
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]

After modification:
[99, 0, 0]
[99, 0, 0]
[99, 0, 0]

Correct initialization:
[99, 0, 0]
[0, 0, 0]
[0, 0, 0]

When to Use 2D Lists

  • Grids and boards (tic-tac-toe, chess, minesweeper)
  • Tables of data (spreadsheets, grade books)
  • Mathematical matrices (linear algebra operations)
  • Images (pixel data with rows and columns)
  • Maps (game levels, grid-based navigation)
# Example: Simple grade book
grade_book = [
    ["Alice", 95, 87, 92],
    ["Bob", 78, 85, 91],
    ["Charlie", 88, 92, 89]
]

print("Grade Book:")
for student in grade_book:
    name = student[0]
    scores = student[1:]
    avg = sum(scores) / len(scores)
    print(f"{name}: scores {scores}, average {avg:.1f}")
Grade Book:
Alice: scores [95, 87, 92], average 91.3
Bob: scores [78, 85, 91], average 84.7
Charlie: scores [88, 92, 89], average 89.7

Summary (1 of 3)

  • A list is a sequence of zero or more elements
  • Lists are mutable data structures
  • index returns position of target element in a list
  • Elements can be arranged in order using sort
  • Mutator methods (insert, append, extend, pop) change the state of an object
  • Assignment of a variable to another causes aliasing (both refer to the same object)

Summary (2 of 3)

  • A tuple is similar to a list, but is immutable
  • A function definition consists of header and body
  • return returns a value from a function
  • A dictionary associates a set of keys with values
  • Use [] to add/access dictionary entries
  • Choose lists for ordered data, dictionaries for key-based lookup

Summary (3 of 3)

  • Creating 2D lists
  • Accessing and modifying elements of a 2D list
  • Traversing with nested loops
  • List comprehension for initilization
  • When to use 2D lists