Data Structures in Python

Learn Data Structures in Python with practical examples, explanations, best practices, and real-world coding techniques.

Data structures in Python are ways of organizing and storing data so that programs can access, modify, search, and process information efficiently. Python provides several built-in collection types, including lists, tuples, sets, and dictionaries, along with standard-library tools that can be used to build more specialized data structures.

Choosing the right data structure is an important part of writing efficient Python programs. A list is useful for ordered sequences, a dictionary is designed for key-value lookups, and a set is useful when uniqueness and membership testing are important. More advanced structures such as heaps, trees, graphs, stacks, and queues are commonly used in algorithms and real-world applications.

Python's Built-in Data Structures

Python includes four fundamental collection types that are used extensively in everyday programming:

Data Structure Ordered Duplicates Typical Use
List Yes Allowed Dynamic ordered collections
Tuple Yes Allowed Fixed or immutable sequences
Set Not used as a sequence No Unique values and membership
Dictionary Insertion order preserved Keys must be unique Key-value lookup

Lists in Python

A Python list is a mutable, ordered collection that can store multiple values. Lists support indexing, slicing, insertion, deletion, and many useful methods.

languages = ["Python", "JavaScript", "Java"]

print(languages[0])
print(languages[1])

languages.append("C#")

print(languages)

Lists can contain values of different types, although using a consistent element type is often easier to maintain.

items = [
    "Python",
    100,
    True,
    {"name": "Alice"}
]

print(items)

Common List Operations

numbers = [10, 20, 30]

numbers.append(40)
numbers.insert(1, 15)
numbers.remove(30)

last = numbers.pop()

print(numbers)
print("Removed:", last)

Python lists are commonly used when you need an ordered collection that can grow or shrink during program execution.

Tuples in Python

A tuple is an ordered, immutable collection. Once created, its elements cannot be changed through normal tuple operations.

coordinates = (10, 20)

print(coordinates[0])
print(coordinates[1])

Tuples are useful for fixed groups of related values, returning multiple values from functions, and representing data that should not be modified accidentally.

Sets in Python

A set stores unique elements. It is useful when duplicate values should be eliminated or when efficient membership testing is required.

numbers = {10, 20, 10, 30, 20}

print(numbers)

print(20 in numbers)

Sets also support mathematical operations such as union, intersection, and difference.

frontend = {"HTML", "CSS", "JavaScript"}
backend = {"Python", "JavaScript", "C#"}

print(frontend | backend)
print(frontend & backend)
print(frontend - backend)

Dictionaries in Python

A dictionary stores data as key-value pairs. It is one of the most important data structures in Python and is commonly used when information needs to be retrieved using a meaningful key.

user = {
    "id": 101,
    "name": "Alice",
    "role": "Developer"
}

print(user["name"])
print(user["role"])

Dictionary values can be updated by assigning a new value to an existing key.

user = {
    "name": "Alice",
    "role": "Developer"
}

user["role"] = "Senior Developer"
user["experience"] = 5

print(user)

Stack in Python

A stack follows the LIFO (Last In, First Out) principle. Python lists can be used as stacks by adding and removing elements from the end.

stack = []

stack.append("A")
stack.append("B")
stack.append("C")

print(stack)

print("Removed:", stack.pop())
print("Top:", stack[-1])

Using append() and pop() at the end of a list provides a simple stack implementation.

Queue in Python

A queue follows the FIFO (First In, First Out) principle. Although a list can represent a queue, repeatedly removing items from the beginning with pop(0) is inefficient for large queues.

Python's collections.deque is designed for efficient insertion and removal from both ends.

from collections import deque

queue = deque()

queue.append("A")
queue.append("B")
queue.append("C")

print(queue)

print("Removed:", queue.popleft())
print("Front:", queue[0])

Linked Lists in Python

Python does not provide a built-in general-purpose linked list type like its built-in list. A linked list can be implemented using custom node objects where each node stores a value and a reference to another node.

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None


first = Node(10)
second = Node(20)
third = Node(30)

first.next = second
second.next = third

current = first

while current:
    print(current.value)
    current = current.next

Linked lists are useful for learning pointer-based data structures and are common in data-structure and algorithm exercises, even though Python's built-in list is preferred for many practical sequence-processing tasks.

Heap in Python

A heap is a tree-based data structure commonly used to implement priority queues. Python provides the heapq module for heap operations. It implements a min-heap using a regular Python list.

import heapq

numbers = [30, 10, 20, 5]

heapq.heapify(numbers)

print(numbers)

heapq.heappush(numbers, 2)

smallest = heapq.heappop(numbers)

print("Smallest:", smallest)

A min-heap keeps the smallest available element at the root position, making it useful for repeatedly retrieving the smallest priority value.

Priority Queue in Python

The heapq module can also be used to create a simple priority queue. A common pattern is to store tuples containing a priority and a value.

import heapq

tasks = []

heapq.heappush(tasks, (2, "Send Email"))
heapq.heappush(tasks, (1, "Fix Critical Bug"))
heapq.heappush(tasks, (3, "Generate Report"))

while tasks:
    priority, task = heapq.heappop(tasks)
    print(priority, task)

Graphs in Python

A graph represents relationships between objects using vertices and edges. Graphs can be represented in Python using dictionaries, lists, sets, or custom classes. An adjacency list is a common representation.

graph = {
    "A": ["B", "C"],
    "B": ["D"],
    "C": ["E"],
    "D": [],
    "E": []
}

for node, neighbors in graph.items():
    print(node, "->", neighbors)

Breadth-First Search with a Queue

Breadth-First Search (BFS) explores a graph level by level. A queue is commonly used to keep track of the nodes that need to be processed.

from collections import deque

graph = {
    "A": ["B", "C"],
    "B": ["D"],
    "C": ["E"],
    "D": [],
    "E": []
}

queue = deque(["A"])
visited = set()

while queue:
    node = queue.popleft()

    if node in visited:
        continue

    visited.add(node)
    print(node)

    for neighbor in graph[node]:
        if neighbor not in visited:
            queue.append(neighbor)

Depth-First Search with a Stack

Depth-First Search (DFS) can be implemented iteratively using a stack. The most recently added node is processed first.

graph = {
    "A": ["B", "C"],
    "B": ["D"],
    "C": ["E"],
    "D": [],
    "E": []
}

stack = ["A"]
visited = set()

while stack:
    node = stack.pop()

    if node in visited:
        continue

    visited.add(node)
    print(node)

    for neighbor in graph[node]:
        if neighbor not in visited:
            stack.append(neighbor)

Trees in Python

A tree is a hierarchical data structure made of nodes connected by edges. Binary trees are a common type where each node can have at most two children.

class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None


root = TreeNode(10)

root.left = TreeNode(5)
root.right = TreeNode(15)

print(root.value)
print(root.left.value)
print(root.right.value)

Common Data Structure Complexity

Understanding the typical complexity of common operations helps when selecting a data structure for an algorithm.

Structure Access Search / Membership Typical Insert
List O(1) O(n) O(1) at end, O(n) elsewhere
Tuple O(1) O(n) Immutable
Set Not index-based O(1) average O(1) average
Dictionary By key O(1) average O(1) average
Heap Root access O(1) O(n) general search O(log n)

These are typical complexity characteristics rather than guarantees for every operation. The actual cost depends on the specific Python data structure and operation being performed.

Choosing the Right Python Data Structure

The best data structure depends on what the application needs to do with the data. Consider the dominant operations rather than choosing a structure simply because it is familiar.

Requirement Possible Choice
Ordered, mutable sequence List
Immutable sequence Tuple
Unique values Set
Key-based lookup Dictionary
FIFO processing deque
LIFO processing List or deque
Priority-based processing heapq

Frequency Counting with a Dictionary

One of the most common data-structure patterns in Python is using a dictionary to count how frequently values occur.

numbers = [10, 20, 10, 30, 20, 10]

frequency = {}

for number in numbers:
    frequency[number] = frequency.get(number, 0) + 1

print(frequency)

Python also provides collections.Counter for this specific task.

from collections import Counter

numbers = [10, 20, 10, 30, 20, 10]

frequency = Counter(numbers)

print(frequency)

Useful Python Collection Tools

Python's collections module provides specialized containers that extend the capabilities of the basic built-in structures.

  • deque for efficient operations at both ends.
  • Counter for counting hashable objects.
  • defaultdict for dictionaries with automatic default values.
  • OrderedDict for specialized ordered dictionary behavior when needed.
  • ChainMap for working with multiple mappings as a single view.

Common Data Structure Mistakes in Python

  • Using a list when fast key-based lookup is the actual requirement.
  • Using pop(0) repeatedly for large queues.
  • Expecting sets to support normal index-based access.
  • Forgetting that tuples are immutable.
  • Assuming dictionary keys can be duplicated.
  • Using mutable objects as dictionary keys or set elements.
  • Searching a list repeatedly when a set or dictionary could provide more appropriate membership or lookup behavior.
  • Ignoring the time and space complexity of a chosen structure in performance-sensitive algorithms.

Practical Example: Processing Developer Skills

Different Python data structures can work together in a single application. For example, a developer profile can use a dictionary for profile information, a list for ordered skills, and a set to determine unique technologies.

developer = {
    "name": "Alice",
    "skills": [
        "Python",
        "JavaScript",
        "Python",
        "SQL"
    ]
}

skills = developer["skills"]

unique_skills = set(skills)

print("Developer:", developer["name"])
print("Skills:", skills)
print("Unique skills:", unique_skills)

if "Python" in unique_skills:
    print("Python is included")

Key Takeaways

  • Python provides powerful built-in data structures such as list, tuple, set, and dictionary.
  • Lists are useful for ordered, mutable sequences.
  • Tuples are ordered and immutable.
  • Sets store unique values and provide efficient average-case membership testing.
  • Dictionaries store key-value pairs and provide efficient average-case key lookup.
  • Lists can implement stacks using append() and pop().
  • deque is a better choice for efficient queue operations at both ends.
  • heapq provides heap-based priority queue functionality.
  • Graphs and trees can be implemented using custom classes and Python collections.
  • Choosing a data structure based on required operations is an important part of algorithm design.

Code Examples

numbers = [10, 20, 30]

numbers.append(40)
numbers.insert(1, 15)

print(numbers)
print("Last:", numbers.pop())
    
user = {
    "id": 101,
    "name": "Alice",
    "role": "Developer"
}

print(user["name"])

user["role"] = "Senior Developer"

print(user)
    
numbers = [10, 20, 10, 30, 20, 40]

unique_numbers = set(numbers)

print(unique_numbers)
print(30 in unique_numbers)
    
from collections import deque

queue = deque()

queue.append("Task A")
queue.append("Task B")
queue.append("Task C")

while queue:
    task = queue.popleft()
    print("Processing:", task)
    
import heapq

tasks = []

heapq.heappush(tasks, (2, "Send Email"))
heapq.heappush(tasks, (1, "Fix Critical Bug"))
heapq.heappush(tasks, (3, "Generate Report"))

while tasks:
    priority, task = heapq.heappop(tasks)
    print(priority, task)
    

Pro Tip

Choose a Python data structure based on the operations your program performs most often. Use list for ordered sequences, dict for key-based lookup, set for uniqueness and membership testing, deque for FIFO queues, and heapq when elements need to be processed by priority.

🎨 IgKnight Docs

Master HTML, CSS, JS, Python, .NET and more with easy-to-follow lessons and practical code examples.

© 2025 IgKnight Learning Platform. All rights reserved.