Chapter 5 — Core Data Structures: Lists, Tuples, Sets, Dictionaries

Data lives in structures. This chapter covers Python's four core containers and shows how to choose the right one — the conceptual bridge to the tabular data structures used throughout the rest of the book.

Learning Objectives

Prerequisites / Imports

Uses the standard copy and time modules.

In [1]:
import copy
import time

1 Lists

Lists are ordered, mutable sequences. Comprehensions build lists concisely.

In [1]:
nums = [3, 1, 4, 1, 5, 9, 2, 6]
nums.append(5)
nums.sort()
print(nums)
print('squares:', [x**2 for x in range(6)])
print('evens:', [x for x in nums if x % 2 == 0])
[1, 1, 2, 3, 4, 5, 5, 6, 9]
squares: [0, 1, 4, 9, 16, 25]
evens: [2, 4, 6]

2 Copying Lists

list.copy() and list() make shallow copies; copy.deepcopy duplicates nested objects.

In [1]:
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0].append(99)
print('original after shallow mutation:', original)

deep = copy.deepcopy(original)
deep[0].append(77)
print('original after deep mutation:', original)
original after shallow mutation: [[1, 2, 99], [3, 4]]
original after deep mutation: [[1, 2, 99], [3, 4]]

3 Searching and Sorting

in tests membership; sorted() returns a new list; .sort() sorts in place.

In [1]:
data = [5, 2, 8, 1, 9, 3]
print('contains 8:', 8 in data)
print('sorted new:', sorted(data, reverse=True))
data.sort()
print('in-place:', data)
contains 8: True
sorted new: [9, 8, 5, 3, 2, 1]
in-place: [1, 2, 3, 5, 8, 9]

4 Tuples

Tuples are immutable and often used for fixed records and multiple return values.

In [1]:
point = (3, 4)
x, y = point            # unpacking
print('distance from origin:', (x**2 + y**2) ** 0.5)

pairs = {(1,'a'), (2,'b')}   # tuples can be set/dict keys
print(pairs)
distance from origin: 5.0
{(2, 'b'), (1, 'a')}

5 Sets

Sets store unique items and support mathematical set operations. Membership is O(1).

In [1]:
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print('union:', a | b)
print('intersection:', a & b)
print('difference:', a - b)
print('symmetric diff:', a ^ b)
union: {1, 2, 3, 4, 5, 6}
intersection: {3, 4}
difference: {1, 2}
symmetric diff: {1, 2, 5, 6}
In [1]:
# Performance: membership in a list vs a set
big_list = list(range(100_000))
big_set = set(big_list)

t0 = time.perf_counter()
_ = 99_999 in big_list
t_list = time.perf_counter() - t0

t0 = time.perf_counter()
_ = 99_999 in big_set
t_set = time.perf_counter() - t0

print(f'list lookup: {t_list*1e6:.1f} us')
print(f'set  lookup: {t_set*1e6:.1f} us')
list lookup: 6936.0 us
set  lookup: 4.4 us

6 Dictionaries

Dictionaries map keys to values. Use .get() for safe access and comprehensions to build them.

In [1]:
prices = {'apple': 1.2, 'banana': 0.5, 'cherry': 3.0}
print(prices.get('banana'), prices.get('date', 'N/A'))
print('uppercased keys:', {k.upper(): v for k, v in prices.items()})
0.5 N/A
uppercased keys: {'APPLE': 1.2, 'BANANA': 0.5, 'CHERRY': 3.0}

7 Choosing the Right Structure

Structure Ordered Mutable Use when
list yes yes sequence of items
tuple yes no fixed record / hashable key
set no yes unique items, fast membership
dict yes* yes key→value mapping

(*dicts preserve insertion order since Python 3.7.)

8 Nesting: The Bridge to Tabular Data

A list of dictionaries models rows of a table — exactly what pandas will formalize.

In [1]:
people = [
    {'name': 'Ada', 'age': 36, 'role': 'engineer'},
    {'name': 'Bo',  'age': 28, 'role': 'analyst'},
    {'name': 'Cy',  'age': 44, 'role': 'manager'},
]
ages = [p['age'] for p in people]
print('ages:', ages)
print('average age:', sum(ages) / len(ages))
ages: [36, 28, 44]
average age: 36.0

Case Study: Inventory Management

Model a store inventory as a list of product dicts with add/sell/search/value operations.

In [1]:
inventory = [
    {'name': 'Widget', 'price': 4.50, 'stock': 10},
    {'name': 'Gadget', 'price': 12.75, 'stock': 5},
    {'name': 'Cable', 'price': 1.20, 'stock': 50},
]

def find(name):
    for p in inventory:
        if p['name'] == name:
            return p
    return None

def sell(name, qty):
    p = find(name)
    if p and p['stock'] >= qty:
        p['stock'] -= qty
        return p['price'] * qty
    return None

def total_value():
    return sum(p['price'] * p['stock'] for p in inventory)

print('Sold 3 Widgets: $', sell('Widget', 3))
print('Sold 2 Gadgets: $', sell('Gadget', 2))
print('Try selling 100 Cables:', sell('Cable', 100))
print('Total inventory value: $', round(total_value(), 2))
for p in inventory:
    print(f"  {p['name']:<8} stock={p['stock']}")
Sold 3 Widgets: $ 13.5
Sold 2 Gadgets: $ 25.5
Try selling 100 Cables: None
Total inventory value: $ 129.75
  Widget   stock=7
  Gadget   stock=3
  Cable    stock=50

Exercises

  1. Use a comprehension to create the squares of 1–10.
  2. Deep-copy a nested list and show the original is unaffected by mutation.
  3. Given two lists, find elements common to both using sets.
  4. Build a dictionary mapping each word in a sentence to its length.
  5. Count word frequencies in a paragraph using a dict.
  6. Remove duplicates from a list using a set, preserving order.
  7. Time membership lookups in a list of 100,000 items vs a set.
  8. Model a phone book as a dict and look up three names.
  9. Convert a list of dicts into a dict of lists (columnar form).
  10. Explain when you would choose a tuple over a list.

Python Data Science: From Foundations to Applications — Chapter 5