3 Compound Objects
In our exploration of Python so far, we’ve worked with fundamental objects that represent single pieces of information: numbers for quantities, strings for text, and booleans for true/false values. However, real-world computational problems often require us to work with multiple related values as a coherent unit.
Consider these examples from different domains. In geometry, a point is defined by its coordinates (x,y). In graphics, a color is specified by its RGB components (red, green, blue). In databases, a record might contain (ID, name, date). In physics, a particle’s state includes (position, velocity, mass).
Each of these concepts is naturally represented as a collection of related values that together form a meaningful unit. We need a way to bundle multiple values together so we can:
- Treat the collection as a single object
- Ensure the values stay together in a meaningful way
- Pass multiple values as a single unit to functions
- Return multiple values from functions
Python provides several ways to create such compound objects, each designed for different use cases. We’ll start with the simplest and most fundamental: tuples.
3.1 Tuples
A tuple is Python’s most basic compound object - a fixed sequence of values that, once created, cannot be changed. This immutability (unchangeability) is a key feature that makes tuples perfect for representing collections of values that logically belong together and shouldn’t change.
Think of a tuple like a sealed package:
- Once you put items in and seal it, the contents are fixed
- You can look inside to see what’s there
- You can pass the whole package around
- But you can’t add, remove, or swap items
This immutability isn’t a limitation - it’s a powerful feature that helps us:
- Express intent - using a tuple says “these values belong together and won’t change”
- Prevent bugs - accidental modifications are impossible
- Enable optimizations - Python can optimize operations on immutable objects
- Build more complex structures - tuples can be used as dictionary keys or set elements
Common uses for tuples include:
- Geometric points: (x, y) or (x, y, z)
- Color values: (red, green, blue)
- Database records: (id, name, email)
- Date/time components: (year, month, day)
- Function return values: (result, error_code)
The fixed nature of tuples reflects the fact that many concepts in computing naturally have a fixed structure - a 2D point always has exactly two coordinates, an RGB color always has exactly three components, etc.
3.1.1 Working with Tuples
Let’s explore how to create and use tuples in practice. Remember that tuples represent fixed collections of values that logically belong together.
3.1.1.1 Creating Tuples
The most straightforward way to create a tuple is to separate values with commas, optionally enclosed in parentheses:
# Basic tuple creation
= (3, 4) # A 2D point
coordinates = (255, 128, 0) # An RGB color value
rgb_color
# Parentheses are optional but recommended for clarity
= 3, 4 # Also creates a tuple point
For single-element tuples, you must include a comma after the value:
= (42,) # Creates a one-element tuple
single = (42) # Just a number in parentheses! not_tuple
This comma requirement might seem strange, but it helps Python distinguish between parentheses used for grouping expressions and those used for creating tuples.
3.1.1.2 Accessing Tuple Elements
Since tuples represent fixed collections of values, each position in a tuple has a specific meaning. You access tuple elements using index numbers in square brackets, starting from 0:
# Consider a point in 3D space
= (10, 20, 30)
point
= point[0] # x coordinate: 10
x = point[1] # y coordinate: 20
y = point[2] # z coordinate: 30 z
You can also use negative indices to count from the end:
= (255, 128, 0)
rgb = rgb[-1] # Last value: 0
blue = rgb[-2] # Second-to-last: 128 green
3.1.1.3 Using Tuples Effectively
Tuples are ideal for representing:
- Coordinates and geometric points:
= (3, 4)
point2d = (3, 4, 5) point3d
- Fixed collections of related values:
= (0, 0, 0)
rgb_black = (255, 255, 255) rgb_white
- Database records or rows:
= ("John", "Doe", "1990-01-01") record
3.1.1.4 Tuple Unpacking
One of tuple’s most useful features is unpacking, where you assign tuple elements to variables in one step:
# Basic unpacking
= (3, 4)
point = point # x = 3, y = 4
x, y
# Unpacking with multiple values
= ("John", 30, "New York")
person = person
name, age, city
# Using * to collect remaining values
= (1, 2, 3, 4, 5)
numbers *rest = numbers
first, second, # first = 1, second = 2, rest = [3, 4, 5]
# Ignoring values with _
= (10, 20, 30, 40)
data = data # Ignore second and fourth values x, _, y, _
3.1.2 Tuple Methods
Since tuples are immutable, they have only two methods:
= (1, 2, 2, 3, 2, 4)
numbers
# count() - count occurrences of an element
= numbers.count(2) # Result: 3
count_2
# index() - find position of first occurrence
= numbers.index(2) # Result: 1 pos_2
3.1.3 Getting Tuple Length
Like other sequences, len()
gives the number of elements:
# Basic length examples
= (10, 20)
point = len(point) # Result: 2
length
= ()
empty = len(empty) # Result: 0 length
3.2 Lists
While tuples provide a way to group related values into an immutable collection, many programming tasks require collections that can be modified after creation. For example, we might need to:
- Add or remove items from a shopping cart during an online purchase
- Update a list of high scores in a game as players achieve better results
- Maintain a growing collection of data points in a scientific experiment
- Track a changing set of active users in a system
For these scenarios, Python provides lists - mutable sequences that can grow or shrink as needed. Like tuples, lists can hold any type of data (numbers, text, or even other lists), but unlike tuples, their contents can be modified after creation.
3.2.1 Creating Lists
The simplest way to create a list is to use square brackets []
with items separated by commas:
# A list of numbers
= [1, 2, 3, 4, 5]
numbers
# A list of strings
= ["apple", "banana", "orange"]
fruits
# A mixed list (can contain different types)
= [1, "hello", 3.14, True] mixed
You can also create an empty list in two ways:
= [] # Using square brackets
empty_list1 = list() # Using the list() function empty_list2
3.2.2 Indexing Lists
In Python, you access list items using their index (position) in the list. Important things to remember: - Indexing starts at 0, not 1 - Negative indices count from the end (-1 is the last item)
= ["red", "green", "blue", "yellow"]
colors
# Positive indexing (from start)
= colors[0] # "red"
first_color = colors[1] # "green"
second_color
# Negative indexing (from end)
= colors[-1] # "yellow"
last_color = colors[-2] # "blue" second_last
Here’s a visual to help understand indexing:
List: ["red", "green", "blue", "yellow"]
Index: 0 1 2 3
Neg: -4 -3 -2 -1
3.2.3 Slicing Lists
Slicing lets you extract multiple items from a list using the format list[start:end]
. The end
index is not included in the slice.
= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers
# Basic slicing
= numbers[0:3] # [0, 1, 2]
first_three = numbers[3:7] # [3, 4, 5, 6]
middle
# Shortcuts
= numbers[:4] # [0, 1, 2, 3]
from_start = numbers[7:] # [7, 8, 9]
until_end = numbers[:] # Makes a copy of the entire list whole_list
You can also use a step value in slices with the format list[start:end:step]
:
= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers
# Every second number
= numbers[::2] # [0, 2, 4, 6, 8]
every_second
# Every third number starting from index 1
= numbers[1:8:3] # [1, 4, 7]
custom_slice
# Reverse the list
= numbers[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] reversed_list
3.2.4 Modifying Lists
Lists are mutable, which means you can change their contents after creation:
# Changing individual elements
= ["apple", "banana", "orange"]
fruits 1] = "grape" # Now: ["apple", "grape", "orange"]
fruits[
# Slice assignment
= [0, 1, 2, 3, 4]
numbers 1:4] = [10, 20, 30] # Replace multiple elements at once
numbers[# Now: [0, 10, 20, 30, 4]
# You can even replace a slice with a different number of elements
1:4] = [50] # Now: [0, 50, 4] numbers[
3.2.5 Adding to Lists
There are several ways to add items to a list:
= ["apple", "banana"]
fruits
# Add one item to the end
"orange") # Now: ["apple", "banana", "orange"]
fruits.append(
# Add multiple items to the end
"grape", "kiwi"])
fruits.extend([# Now: ["apple", "banana", "orange", "grape", "kiwi"]
# Insert at a specific position
1, "mango") # Insert at index 1
fruits.insert(# Now: ["apple", "mango", "banana", "orange", "grape", "kiwi"]
3.2.6 Getting List Length
The len()
function is used to find out how many items are in a list. It’s a built-in Python function that works with many types of objects, but with lists it simply counts the number of elements:
# Basic length examples
= [1, 2, 3, 4, 5]
numbers = len(numbers) # Result: 5
length
= []
empty_list = len(empty_list) # Result: 0
length
= [1, "hello", True, 3.14]
mixed = len(mixed) # Result: 4 length
The len()
function is particularly useful when you need to:
- Check if a list is empty (length is 0)
- Process the last few elements of a list
- Iterate through list indices
- Verify the size of a list before performing operations
3.2.7 List Comprehensions
List comprehensions provide a concise way to create lists based on existing lists or other sequences. They’re like a compact for-loop that creates a new list. Let’s break down the syntax step by step.
3.2.7.1 Basic Syntax
The basic format is:
for item in sequence] [expression
This consists of three parts:
expression
: What to put in the new list - this is where you use theitem
variable to specify what you want to do with each elementitem
: A variable name you choose that will represent each element as Python goes through the sequence - this name is then used in the expression to say what to do with each elementsequence
: The list or other sequence you’re iterating over
The item
variable is like a temporary name that holds each element from your sequence one at a time, so you can use that name in your expression to say what you want to do with it. For example:
# In [x**2 for x in range(5)]:
# - x is the item variable (could be any name)
# - range(5) is the sequence [0,1,2,3,4]
# - x**2 is the expression that uses x to square each number
= [x**2 for x in range(5)]
squares # Python goes through each number in range(5)
# puts it in x, then puts x**2 in the new list
# Result: [0, 1, 4, 9, 16]
3.2.7.2 Adding Conditions
You can add an if condition to filter items:
for item in sequence if condition] [expression
The parts are:
expression
: What to put in the new listitem
: Variable for each elementsequence
: What you’re iterating overif condition
: Only include items that make this condition True
Example with filtering:
# Only include even numbers
= [1, 2, 3, 4, 5, 6]
numbers = [x for x in numbers if x % 2 == 0]
evens # Result: [2, 4, 6]
# Same as this traditional for loop:
= []
evens for x in numbers:
if x % 2 == 0:
evens.append(x)
3.2.7.3 Real-World Examples
Temperature conversion:
# Convert Celsius temperatures to Fahrenheit
= [0, 10, 20, 30]
celsius = [c * 9/5 + 32 for c in celsius]
fahrenheit # Result: [32.0, 50.0, 68.0, 86.0]
Working with strings:
# Get lengths of words
= ["hello", "world", "python"]
words = [len(word) for word in words]
lengths # Result: [5, 5, 6]
# Convert words to uppercase
= [word.upper() for word in words]
upper_words # Result: ["HELLO", "WORLD", "PYTHON"]
Filtering with conditions:
# Get words longer than 4 letters
= ["cat", "dog", "elephant", "bird", "giraffe"]
words = [word for word in words if len(word) > 4]
long_words # Result: ["elephant", "giraffe"]
# Square numbers greater than 2
= [x**2 for x in range(6) if x > 2]
squares # Result: [9, 16, 25]
Remember:
- List comprehensions make code shorter but shouldn’t be too complex
- If your list comprehension is hard to read, it’s better to use a regular for loop
- They’re great for simple transformations and filtering
3.3 Dictionaries
Dictionaries are another fundamental data structure in Python. Think of a dictionary like a real-world dictionary where you look up words (keys) to find their definitions (values). In Python, dictionaries let you store and retrieve values using unique keys, making them perfect for organizing related data.
3.3.1 Creating Dictionaries
The simplest way to create a dictionary is to use curly braces {}
with key-value pairs separated by commas:
# A simple dictionary
= {"name": "John", "age": 30, "city": "New York"}
person
# A dictionary with different value types
= {
mixed "number": 42,
"text": "hello",
"list": [1, 2, 3],
"bool": True
}
You can also create an empty dictionary in two ways:
= {} # Using curly braces
empty_dict1 = dict() # Using the dict() function empty_dict2
3.3.2 Accessing Values
You access dictionary values using their keys in square brackets or with the get()
method:
= {"name": "John", "age": 30, "city": "New York"}
person
# Using square brackets
= person["name"] # "John"
name = person["age"] # 30
age
# Using get() method (safer - returns None if key doesn't exist)
= person.get("city") # "New York"
city = person.get("country") # None
country # You can specify a default value
= person.get("country", "Unknown") # "Unknown" country
3.3.3 Modifying Dictionaries
Dictionaries are mutable, so you can change, add, or remove key-value pairs:
= {"name": "John", "age": 30}
person
# Changing existing values
"age"] = 31 # Update age
person[
# Adding new key-value pairs
"city"] = "Boston" # Add new pair
person[
# Updating multiple pairs at once
person.update({"age": 32,
"job": "Engineer",
"city": "Seattle"
})
# Removing items
= person.pop("age") # Removes and returns the value
removed_age del person["city"] # Removes the key-value pair
3.3.4 Dictionary Methods
Common dictionary methods for working with keys and values:
= {
person "name": "John",
"age": 30,
"city": "New York"
}
# Get all keys
= person.keys() # dict_keys(['name', 'age', 'city'])
keys
# Get all values
= person.values() # dict_values(['John', 30, 'New York'])
values
# Get all key-value pairs as tuples
= person.items() # dict_items([('name', 'John'), ('age', 30), ('city', 'New York')])
items
# Check if key exists
= "name" in person # True
has_name = "country" in person # False has_country
3.3.5 Getting Dictionary Length
Like lists, the len()
function counts the number of key-value pairs in a dictionary:
# Basic length examples
= {"name": "John", "age": 30, "city": "New York"}
person = len(person) # Result: 3
length
= {}
empty_dict = len(empty_dict) # Result: 0 length
3.3.6 Dictionary Comprehensions
Similar to list comprehensions, dictionary comprehensions provide a concise way to create dictionaries:
# Basic syntax:
# {key_expression: value_expression for item in sequence}
# Create a dictionary of number squares
= {x: x**2 for x in range(5)}
squares # Result: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# Create from two lists
= ["a", "b", "c"]
keys = [1, 2, 3]
values = {k: v for k, v in zip(keys, values)}
dict_from_lists # Result: {"a": 1, "b": 2, "c": 3}
# With conditions
= {x: x**2 for x in range(6) if x % 2 == 0}
even_squares # Result: {0: 0, 2: 4, 4: 16}
3.3.7 Real-World Examples
Here are some practical examples of using dictionaries:
# Student grades
= {
grades "Alice": {"math": 90, "science": 95, "history": 88},
"Bob": {"math": 85, "science": 92, "history": 94},
"Charlie": {"math": 88, "science": 87, "history": 90}
}
# Calculate average grade for each student
= {
averages sum(scores.values()) / len(scores)
student: for student, scores in grades.items()
}
# Configuration settings
= {
config "database": {
"host": "localhost",
"port": 5432,
"name": "myapp"
},"api": {
"url": "https://api.example.com",
"key": "abc123",
"timeout": 30
} }
Remember:
- Dictionary keys must be immutable (strings, numbers, tuples)
- Each key must be unique within the dictionary
- Values can be of any type, including other dictionaries
- Dictionaries are perfect for structured data with named fields
3.4 Sets
Sets are unordered collections of unique elements in Python. Think of a set like a bag that can hold items, but each item can only appear once, and the order doesn’t matter. Sets are perfect for removing duplicates from data and performing mathematical set operations like unions and intersections.
3.4.1 Creating Sets
You can create a set using curly braces {}
(like dictionaries, but without key-value pairs) or the set()
function:
# Creating sets with curly braces
= {"apple", "banana", "orange"}
fruits
# Creating a set from a list (removes duplicates)
= set([1, 2, 2, 3, 3, 4]) # Result: {1, 2, 3, 4}
numbers
# Mixed types are allowed
= {42, "hello", True, 3.14} mixed
You can create an empty set using the set()
function (empty curly braces {}
create an empty dictionary):
= set() # Create empty set empty_set
3.4.2 Set Operations
Sets support mathematical set operations like union, intersection, and difference:
= {1, 2, 3, 4}
set1 = {3, 4, 5, 6}
set2
# Union (all elements from both sets)
= set1 | set2 # {1, 2, 3, 4, 5, 6}
union # Alternative: set1.union(set2)
# Intersection (elements common to both sets)
= set1 & set2 # {3, 4}
intersection # Alternative: set1.intersection(set2)
# Difference (elements in set1 but not in set2)
= set1 - set2 # {1, 2}
difference # Alternative: set1.difference(set2)
# Symmetric difference (elements in either set, but not both)
= set1 ^ set2 # {1, 2, 5, 6}
sym_diff # Alternative: set1.symmetric_difference(set2)
3.4.3 Modifying Sets
Sets are mutable, so you can add or remove elements:
= {"red", "green", "blue"}
colors
# Add single element
"yellow") # Now: {"red", "green", "blue", "yellow"}
colors.add(
# Add multiple elements
"orange", "purple"])
colors.update([# Now: {"red", "green", "blue", "yellow", "orange", "purple"}
# Remove element (raises KeyError if not found)
"blue")
colors.remove(
# Remove element (no error if not found)
"black") # No error if "black" isn't in the set
colors.discard(
# Remove and return an arbitrary element
= colors.pop()
popped
# Remove all elements
# Now: set() colors.clear()
3.4.4 Set Methods
Common set methods for checking membership and relationships:
= {1, 2, 3, 4}
set1 = {3, 4}
set2 = {5, 6}
set3
# Check if element exists
= 1 in set1 # True
has_one = 5 in set1 # False
has_five
# Check if one set is subset of another
= set2 <= set1 # True
is_subset # Alternative: set2.issubset(set1)
# Check if one set is superset of another
= set1 >= set2 # True
is_superset # Alternative: set1.issuperset(set2)
# Check if sets are disjoint (no common elements)
= set1.isdisjoint(set3) # True are_disjoint
3.4.5 Getting Set Length
The len()
function counts the number of unique elements in a set:
# Basic length examples
= {1, 2, 3, 4, 5}
numbers = len(numbers) # Result: 5
length
= set()
empty_set = len(empty_set) # Result: 0
length
# Creating set from list with duplicates
= set([1, 2, 2, 3, 3, 3])
items = len(items) # Result: 3 (unique elements only) length
3.4.6 Set Comprehensions
Like lists and dictionaries, sets support comprehensions for creating sets concisely:
# Basic syntax:
# {expression for item in sequence}
# Create a set of squares
= {x**2 for x in range(5)}
squares # Result: {0, 1, 4, 9, 16}
# With conditions
= {x**2 for x in range(6) if x % 2 == 0}
even_squares # Result: {0, 4, 16}
# Create set from string characters
= {char for char in "hello world" if char in 'aeiou'}
vowels # Result: {'e', 'o'}
3.4.7 Real-World Examples
Here are some practical examples of using sets:
# Remove duplicates from a list
= [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
numbers = list(set(numbers))
unique_numbers # Result: [1, 2, 3, 4]
# Find unique words in text
= "the quick brown fox jumps over the lazy dog"
text = set(text.split())
unique_words # Result: {'the', 'quick', 'brown', 'fox', 'jumps', 'over', 'lazy', 'dog'}
# Find common tags between posts
= {"python", "programming", "tutorial"}
post1_tags = {"python", "beginner", "tutorial"}
post2_tags = post1_tags & post2_tags
common_tags # Result: {"python", "tutorial"}
# Check valid moves in a game
= {"up", "down", "left", "right"}
valid_moves def is_valid_move(move):
return move.lower() in valid_moves
Remember:
- Sets are unordered - don’t rely on element order
- Sets can only contain immutable elements (no lists or dictionaries)
- Sets are perfect for removing duplicates
- Set operations are very efficient for large collections
- Use sets when order doesn’t matter and uniqueness does
3.5 Exercises
List Operations
Given this list:
= ["apple", "banana", "orange"] fruits
What will be printed by:
print(fruits[0])
print(fruits[-1])
print(fruits[1:])
Explain why each result occurs.
Tuple Unpacking
Given these tuples:
= (3, 4) point = ("Alice", 25, "New York") person
Write code to:
- Unpack
point
into variablesx
andy
- Unpack
person
into variablesname
,age
, andcity
- Print formatted strings using the unpacked variables
- Unpack
Dictionary Methods
Given this dictionary:
= { student "name": "Bob", "age": 20, "grade": "A" }
How would you:
- Get the value of “name”
- Safely get a non-existent key “score” with a default value
- Get all keys in the dictionary
Set Operations
Create two sets:
= {1, 2, 3} set1 = {3, 4, 5} set2
Calculate and explain:
- The union of set1 and set2
- The intersection of set1 and set2
- The difference between set1 and set2
Nested Data Structures
Given this nested structure:
= { data "numbers": [1, 2, 3], "point": (4, 5), "valid": True }
How would you access:
- The first number in the “numbers” list
- The second value in the “point” tuple
- The boolean value
List to Dictionary Conversion
Given these lists:
= ["Alice", "Bob", "Charlie"] names = [25, 30, 35] ages
Write code to create a dictionary where names are keys and ages are values.
Set and String Operations
Given:
= {"a", "e", "i", "o", "u"} vowels = "hello" text
Write code to find which vowels appear in the text.
Dictionary Manipulation
Starting with:
= {"name": "Alice", "age": 25} profile
Write code to:
- Add a new key “city” with value “Boston”
- Update multiple values using the update() method
- Print the modified dictionary