Optional: Intermediate List Techniques
Explore nested lists, advanced slicing techniques, and Python tuples. Learn when and why to use these structures for more efficient data handling.
Introduction
Everything you've learned in the previous three lessons is enough to start building real automation scripts. You can create lists, modify them, and use methods to organize and analyze data.
This lesson is optional. It covers techniques that aren't essential for basic workflows but become useful as your scripts get more complex.
Come back to this lesson when you need these techniques. You'll know when that is — either because you hit a problem that these solve, or because you see them in someone else's code and want to understand what's happening.
Lesson Overview
This section contains a general overview of topics you will learn in this lesson.
- Lists containing lists (nested lists)
- Advanced slicing techniques
- Introduction to tuples (immutable lists)
- When you might need these techniques
Lists of Lists
A list can contain other lists. This is called a nested list or a list of lists.
Basic Example
# Rooms organized by floor
floor_1_rooms = ["Office 1", "Office 2"]
floor_2_rooms = ["Office 3", "Office 4"]
# Store both floors in one list
all_floors = [floor_1_rooms, floor_2_rooms]
print(all_floors)
# Output: [["Office 1", "Office 2"], ["Office 3", "Office 4"]]
Now all_floors is a list containing two lists.
Accessing Items
To access items in a nested list, use multiple indexes.
all_floors = [["Office 1", "Office 2"], ["Office 3", "Office 4"]]
# Get the first floor
first_floor = all_floors[0]
print(first_floor)
# Output: ["Office 1", "Office 2"]
# Get a specific room
room = all_floors[0][1]
print(room)
# Output: Office 2
The syntax all_floors[0][1] means:
all_floors[0]→ Get the first floor list[1]→ Get the second item in that list
Architectural Example: Organizing Rooms by Floor
basement_rooms = ["Storage", "Mechanical"]
ground_rooms = ["Lobby", "Reception", "Office 1"]
first_rooms = ["Office 2", "Office 3", "Conference"]
building = [basement_rooms, ground_rooms, first_rooms]
# How many floors?
floor_count = len(building)
print(f"Floors: {floor_count}")
# Output: Floors: 3
# How many rooms on ground floor?
ground_floor_room_count = len(building[1])
print(f"Ground floor rooms: {ground_floor_room_count}")
# Output: Ground floor rooms: 3
# Print all floors and their rooms
for i, floor_rooms in enumerate(building):
print(f"Floor {i}: {floor_rooms}")
# Output:
# Floor 0: ["Storage", "Mechanical"]
# Floor 1: ["Lobby", "Reception", "Office 1"]
# Floor 2: ["Office 2", "Office 3", "Conference"]
(The enumerate() function gives you both the index and the item. You'll see this more in Module 05.)
When to Use Lists of Lists
Lists of lists are useful when your data has natural layers or groups:
- Rooms organized by floor
- Sheets organized by discipline
- Coordinates (list of [x, y, z] points)
- Schedule data (list of rows, each row is a list of values)
Example: Sheet organization
arch_sheets = ["A-101", "A-102", "A-201"]
struct_sheets = ["S-101", "S-102"]
mep_sheets = ["M-101", "E-101"]
all_sheets = [arch_sheets, struct_sheets, mep_sheets]
# Total sheets across all disciplines
total = sum(len(discipline) for discipline in all_sheets)
print(f"Total sheets: {total}")
# Output: Total sheets: 7
Advanced Slicing
You've already seen basic slicing: my_list[start:end]. There's more you can do.
Step Values
You can specify a step value: my_list[start:end:step]
sheets = ["A-101", "A-102", "A-201", "A-202", "S-101", "S-102"]
# Every other item
every_other = sheets[::2]
print(every_other)
# Output: ["A-101", "A-201", "S-101"]
# Every third item
every_third = sheets[::3]
print(every_third)
# Output: ["A-101", "A-202"]
The syntax is [start:end:step]. If you omit start and end, it means "entire list."
Reversing with Slicing
A negative step reverses the list.
sheets = ["A-101", "A-102", "A-201", "A-202"]
reversed_sheets = sheets[::-1]
print(reversed_sheets)
# Output: ["A-202", "A-201", "A-102", "A-101"]
This creates a new reversed list without modifying the original.
Compare to .reverse():
sheets.reverse() # Modifies original
vs
reversed_sheets = sheets[::-1] # Creates new list, original unchanged
Copying a List
You can copy a list using slicing.
original = ["A-101", "A-102", "A-201"]
# Copy the list
copy = original[:]
# Modify the copy
copy.append("A-202")
print(original) # Output: ["A-101", "A-102", "A-201"] (unchanged)
print(copy) # Output: ["A-101", "A-102", "A-201", "A-202"]
Why does this matter? Because assigning lists doesn't create a copy:
original = ["A-101", "A-102"]
reference = original # This does NOT create a copy
reference.append("A-201")
print(original) # Output: ["A-101", "A-102", "A-201"] (changed!)
print(reference) # Output: ["A-101", "A-102", "A-201"]
Both variables point to the same list. Changes to one affect the other.
To create a true copy:
copy = original[:]
# or
copy = original.copy()
# or
copy = list(original)
All three methods create an independent copy.
Architectural Example: Extracting Ranges
sheets = ["A-101", "A-102", "A-201", "A-202", "S-101", "S-102", "M-101"]
# First 4 sheets
first_four = sheets[:4]
print(first_four)
# Output: ["A-101", "A-102", "A-201", "A-202"]
# Last 3 sheets
last_three = sheets[-3:]
print(last_three)
# Output: ["S-102", "M-101"]
# Middle section
middle = sheets[2:5]
print(middle)
# Output: ["A-201", "A-202", "S-101"]
Tuples (Immutable Lists)
A tuple is like a list, but it can't be changed after creation. It's immutable.
Creating Tuples
Tuples use parentheses instead of square brackets.
# List (mutable)
room_list = ["Office 1", "Office 2"]
# Tuple (immutable)
room_tuple = ("Office 1", "Office 2")
You can access items the same way:
project_info = ("2024-001", "Community Center", "In Progress")
project_code = project_info[0]
print(project_code) # Output: 2024-001
But you can't modify them:
project_info[0] = "2024-002"
# Error: TypeError: 'tuple' object does not support item assignment
You also can't add or remove items:
project_info.append("Approved")
# Error: AttributeError: 'tuple' object has no attribute 'append'
Why Use Tuples?
Tuples are for data that shouldn't change.
Use tuples when:
- The data is fixed (coordinates, project metadata)
- You want to prevent accidental modification
- You're returning multiple values from a function (Module 06)
Examples:
# Project metadata (shouldn't change)
project = ("2024-001", "Community Center", "Smith Architects")
# A point in 3D space
point = (125.3, 78.9, 45.2)
# RGB color values
color = (255, 128, 0)
Unpacking Tuples
You can assign tuple values to multiple variables at once.
project_info = ("2024-001", "Community Center", "In Progress")
# Unpack into three variables
project_code, project_name, status = project_info
print(project_code) # Output: 2024-001
print(project_name) # Output: Community Center
print(status) # Output: In Progress
This works with lists too, but it's most common with tuples.
Architectural Example:
# Point coordinates
point = (125.3, 78.9, 45.2)
x, y, z = point
print(f"X: {x}, Y: {y}, Z: {z}")
# Output: X: 125.3, Y: 78.9, Z: 45.2
Tuples vs Lists: When to Use Each
Use lists when:
- Data might change (add, remove, modify items)
- You're building a collection dynamically
- You need list methods like
.sort()or.append()
Use tuples when:
- Data is fixed and shouldn't change
- You want to ensure immutability
- You're working with coordinates or structured records
In practice: You'll use lists 90% of the time. Tuples are for special cases.
Real Examples
Lists of Lists: Room Schedule
# Each room: [name, area, type]
rooms = [
["Office 1", 12.5, "Private"],
["Office 2", 15.3, "Private"],
["Conference", 45.8, "Meeting"],
["Break Room", 18.2, "Common"]
]
# Total area
total_area = sum(room[1] for room in rooms)
print(f"Total area: {total_area}m²")
# Output: Total area: 91.8m²
# Print each room
for room in rooms:
name = room[0]
area = room[1]
room_type = room[2]
print(f"{name}: {area}m² ({room_type})")
# Output:
# Office 1: 12.5m² (Private)
# Office 2: 15.3m² (Private)
# Conference: 45.8m² (Meeting)
# Break Room: 18.2m² (Common)
Tuples: Fixed Project Data
# Project metadata (shouldn't change)
project = ("2024-001", "Community Center", "Smith Architects", "In Progress")
# Unpack
code, name, architect, status = project
print(f"Project: {name}")
print(f"Code: {code}")
print(f"Architect: {architect}")
print(f"Status: {status}")
# Output:
# Project: Community Center
# Code: 2024-001
# Architect: Smith Architects
# Status: In Progress
Copying Lists Safely
original_sheets = ["A-101", "A-102", "A-201"]
# Wrong - creates reference, not copy
wrong_copy = original_sheets
wrong_copy.append("A-202")
print(original_sheets)
# Output: ["A-101", "A-102", "A-201", "A-202"] (modified!)
# Correct - creates independent copy
original_sheets = ["A-101", "A-102", "A-201"]
correct_copy = original_sheets[:]
correct_copy.append("A-202")
print(original_sheets)
# Output: ["A-101", "A-102", "A-201"] (unchanged)
When You'll Need These Techniques
Lists of lists:
- When you start working with schedule data from Revit
- When organizing elements by category or level
- When processing CSV files (rows of data)
Advanced slicing:
- When you need to copy lists
- When extracting specific ranges
- When reversing without mutation
Tuples:
- When working with coordinates
- When functions return multiple values (Module 06)
- When you want to prevent accidental changes
You don't need to master these now. Just know they exist. When you encounter a situation where they'd help, you'll remember this lesson and come back.
Assignment
- Create a new file called
intermediate_lists.py - Create a list of lists for three floors:
- Basement:
["Storage", "Mechanical"] - Ground:
["Lobby", "Office 1", "Office 2"] - First:
["Office 3", "Conference"]
- Basement:
- Store all three in a
buildinglist - Access and print:
- The second floor (Ground)
- The first room on the ground floor ("Lobby")
- The total number of floors
- The total number of rooms (sum across all floors)
- Create a list:
sheets = ["A-101", "A-102", "A-201", "A-202", "S-101", "S-102"] - Use slicing to:
- Get every other sheet
- Reverse the list (without modifying the original)
- Create a copy of the list
- Create a tuple for project info:
("2024-001", "Community Center", "Active") - Unpack the tuple into three variables and print each
- Try to modify the tuple (append or change an item) — see the error
- Experiment:
- Create a list and assign it to another variable
- Modify the second variable
- See that both variables changed (they reference the same list)
- Now create a proper copy using
[:]and verify it's independent
Knowledge Check
The following questions are an opportunity to reflect on key topics in this lesson.
- How do you access an item in a nested list?
- What does
my_list[::2]do? - How do you reverse a list without modifying the original?
- How do you create an independent copy of a list?
- What's the difference between a list and a tuple?
- What is tuple unpacking?
- When would you use a tuple instead of a list?
Additional Resources
This section contains helpful links to related content. It isn't required, so consider it supplemental.
- Python's data structures tutorial covers lists, tuples, and more
- Real Python's guide to nested lists has additional examples
- Understanding slicing provides detailed explanation of slice syntax