Data Structures in Python

Python provides several built-in data structures for storing and organizing data.

Lists

Lists are ordered, mutable sequences that can contain different types of elements.

Creating Lists

# Empty list
empty_list = []

# List with elements
numbers = [1, 2, 3, 4, 5]
fruits = ["apple", "banana", "cherry"]
mixed = [1, "hello", 3.14, True]

List Operations

fruits = ["apple", "banana", "cherry"]

# Access elements
print(fruits[0])  # apple
print(fruits[-1]) # cherry

# Modify elements
fruits[1] = "orange"
print(fruits)  # ['apple', 'orange', 'cherry']

# Add elements
fruits.append("grape")
print(fruits)  # ['apple', 'orange', 'cherry', 'grape']

# Insert at specific position
fruits.insert(1, "mango")
print(fruits)  # ['apple', 'mango', 'orange', 'cherry', 'grape']

# Remove elements
fruits.remove("orange")
print(fruits)  # ['apple', 'mango', 'cherry', 'grape']

# Remove by index
popped = fruits.pop(2)
print(popped)  # cherry
print(fruits)  # ['apple', 'mango', 'grape']

List Slicing

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

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

List Methods

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

# Sort the list
numbers.sort()
print(numbers)  # [1, 1, 2, 3, 4, 5, 6, 9]

# Reverse the list
numbers.reverse()
print(numbers)  # [9, 6, 5, 4, 3, 2, 1, 1]

# Find index of element
print(numbers.index(5))  # 2

# Count occurrences
print(numbers.count(1))  # 2

# Get length
print(len(numbers))  # 8

Tuples

Tuples are ordered, immutable sequences.

Creating Tuples

# Empty tuple
empty_tuple = ()

# Tuple with elements
coordinates = (10, 20)
person = ("Alice", 25, "Engineer")

# Single element tuple (note the comma)
single = (5,)

Tuple Operations

coordinates = (10, 20, 30)

# Access elements
print(coordinates[0])  # 10
print(coordinates[-1]) # 30

# Slicing
print(coordinates[1:])  # (20, 30)

# Unpacking
x, y, z = coordinates
print(x, y, z)  # 10 20 30

Dictionaries

Dictionaries store key-value pairs and are unordered.

Creating Dictionaries

# Empty dictionary
empty_dict = {}

# Dictionary with data
student = {
    "name": "Alice",
    "age": 20,
    "grade": "A"
}

# Using dict() constructor
person = dict(name="Bob", age=25, city="New York")

Dictionary Operations

student = {"name": "Alice", "age": 20, "grade": "A"}

# Access values
print(student["name"])  # Alice
print(student.get("age"))  # 20

# Add new key-value pair
student["major"] = "Computer Science"
print(student)

# Modify existing value
student["age"] = 21
print(student)

# Remove key-value pair
del student["grade"]
print(student)

# Check if key exists
print("name" in student)  # True

# Get all keys
print(student.keys())  # dict_keys(['name', 'age', 'major'])

# Get all values
print(student.values())  # dict_values(['Alice', 21, 'Computer Science'])

# Get all key-value pairs
print(student.items())  # dict_items([('name', 'Alice'), ('age', 21), ('major', 'Computer Science')])

Dictionary Example: Student Records

students = {
    "001": {"name": "Alice", "grade": "A"},
    "002": {"name": "Bob", "grade": "B"},
    "003": {"name": "Charlie", "grade": "A"}
}

# Access nested dictionary
print(students["001"]["name"])  # Alice

# Add new student
students["004"] = {"name": "David", "grade": "B"}

# Update grade
students["002"]["grade"] = "A"

Dictionary Example: Word Frequency Counter

text = "hello world hello python world"
words = text.split()

word_count = {}
for word in words:
    if word in word_count:
        word_count[word] += 1
    else:
        word_count[word] = 1

print(word_count)  # {'hello': 2, 'world': 2, 'python': 1}

Sets

Sets are unordered collections of unique elements.

Creating Sets

# Empty set
empty_set = set()

# Set with elements
numbers = {1, 2, 3, 4, 5}
fruits = {"apple", "banana", "cherry"}

# From list (removes duplicates)
numbers = set([1, 2, 2, 3, 3, 3])
print(numbers)  # {1, 2, 3}

Set Operations

set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}

# Add element
set1.add(5)
print(set1)  # {1, 2, 3, 4, 5}

# Remove element
set1.remove(5)
print(set1)  # {1, 2, 3, 4}

# Union
print(set1 | set2)  # {1, 2, 3, 4, 5, 6}

# Intersection
print(set1 & set2)  # {3, 4}

# Difference
print(set1 - set2)  # {1, 2}

# Symmetric difference
print(set1 ^ set2)  # {1, 2, 5, 6}

Data Structure Comparison

Advanced List Operations

List Comprehensions

# Create list of squares
squares = [x**2 for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# Filter even numbers
evens = [x for x in range(10) if x % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8]

# Nested comprehensions
matrix = [[i*j for j in range(3)] for i in range(3)]
print(matrix)  # [[0, 0, 0], [0, 1, 2], [0, 2, 4]]

List as Stack and Queue

# Stack (LIFO)
stack = []
stack.append(1)  # Push
stack.append(2)
stack.append(3)
print(stack.pop())  # 3 (Pop)

# Queue (FIFO) - inefficient for large lists
queue = []
queue.append(1)  # Enqueue
queue.append(2)
queue.append(3)
print(queue.pop(0))  # 1 (Dequeue)

Advanced Dictionary Operations

Dictionary Comprehensions

# Create dict of squares
squares = {x: x**2 for x in range(5)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# Filter dictionary
grades = {'Alice': 85, 'Bob': 92, 'Charlie': 78}
passed = {name: grade for name, grade in grades.items() if grade >= 80}
print(passed)  # {'Alice': 85, 'Bob': 92}

Merging Dictionaries

dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}

# Python 3.9+
merged = dict1 | dict2
print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

# Update method
dict1.update(dict2)
print(dict1)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

Default Values with defaultdict

from collections import defaultdict

# Default list
word_groups = defaultdict(list)
words = ['apple', 'banana', 'cherry', 'apricot']
for word in words:
    word_groups[word[0]].append(word)

print(word_groups)  # {'a': ['apple', 'apricot'], 'b': ['banana'], 'c': ['cherry']}

# Default counter
word_count = defaultdict(int)
text = "hello world hello python"
for word in text.split():
    word_count[word] += 1

print(word_count)  # defaultdict(<class 'int'>, {'hello': 2, 'world': 1, 'python': 1})

Advanced Set Operations

Set Comprehensions

# Create set of squares
squares = {x**2 for x in range(10)}
print(squares)  # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}

# Filter unique even numbers
numbers = [1, 2, 2, 3, 4, 4, 5]
evens = {x for x in numbers if x % 2 == 0}
print(evens)  # {2, 4}

Frozen Sets (Immutable Sets)

# Create frozen set
frozen = frozenset([1, 2, 3, 4])
print(frozen)  # frozenset({1, 2, 3, 4})

# Can be used as dictionary keys
my_dict = {frozen: "value"}
print(my_dict[frozen])  # value

Common Data Structure Use Cases

Lists

  • Storing sequences of items

  • Implementing stacks and queues

  • Matrix operations

  • Dynamic arrays

Tuples

  • Returning multiple values from functions

  • Dictionary keys (immutable)

  • Unpacking assignments

  • Fixed configuration data

Dictionaries

  • Key-value data storage

  • Caching results

  • Counting frequencies

  • Configuration settings

Sets

  • Removing duplicates

  • Membership testing

  • Mathematical set operations

  • Finding unique elements

Performance Considerations

Time Complexity

Operation

List

Tuple

Dict

Set

Access by index

O(1)

O(1)

O(1)

N/A

Search

O(n)

O(n)

O(1)

O(1)

Insert

O(n)

N/A

O(1)

O(1)

Delete

O(n)

N/A

O(1)

O(1)

Space Complexity

  • Lists: O(n)

  • Tuples: O(n)

  • Dictionaries: O(n)

  • Sets: O(n)

Tips

  • Use lists for ordered collections you need to modify

  • Use tuples for immutable sequences

  • Use dictionaries for fast lookups by key

  • Use sets for unique items and set operations

  • Consider collections.deque for efficient queue operations

  • Use collections.Counter for counting frequencies