Skip to main content
Python beginner Lesson 13 of 28

Python Collections

Deep dive into list, dict, set, tuple, and the powerful collections module: deque, defaultdict, Counter, and namedtuple.

list

Lists are ordered, mutable sequences. The most-used collection in Python.

items = [1, 2, 3, "four", 5.0]   # heterogeneous is fine

# Indexing and slicing
items[0]       # 1
items[-1]      # 5.0
items[1:3]     # [2, 3]

# Mutating
items.append(6)            # add to end
items.insert(2, "two.5")   # insert at index 2
items.extend([7, 8])       # add multiple
items.remove("four")       # remove first occurrence
popped = items.pop()       # remove and return last
popped2 = items.pop(0)     # remove and return by index

# Sorting
nums = [3, 1, 4, 1, 5, 9]
nums.sort()                         # in-place
nums.sort(reverse=True)
sorted_copy = sorted(nums)          # returns new list

# Sort by key
people = [{"name": "Bob", "age": 25}, {"name": "Alice", "age": 30}]
people.sort(key=lambda p: p["age"])

# Other
nums.count(1)    # 2
nums.index(5)    # first index of 5
nums.reverse()   # in-place reverse
nums.copy()      # shallow copy

dict

Dictionaries are ordered (since Python 3.7), mutable key-value maps.

user = {"name": "Alice", "age": 30, "active": True}

# Access
user["name"]               # "Alice"
user.get("email")          # None  (no KeyError)
user.get("email", "N/A")   # "N/A"  (default)

# Mutate
user["email"] = "[email protected]"
user.update({"city": "NYC", "age": 31})

# Delete
del user["active"]
email = user.pop("email", None)   # remove and return

# Iterate
for key in user:                  # iterate keys
    ...
for key, value in user.items():   # iterate pairs
    ...
for value in user.values():       # iterate values
    ...

# Merge dicts (Python 3.9+)
defaults = {"timeout": 30, "retries": 3}
config = {"timeout": 60}
merged = defaults | config        # {"timeout": 60, "retries": 3}

# Dict comprehension
squares = {x: x**2 for x in range(6)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

set

Sets are unordered collections of unique, hashable elements.

tags = {"python", "web", "api"}
tags.add("async")
tags.discard("web")          # remove if present (no error)
tags.remove("api")           # remove (raises KeyError if missing)

# Set operations
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

a | b    # {1, 2, 3, 4, 5, 6}  union
a & b    # {3, 4}               intersection
a - b    # {1, 2}               difference
a ^ b    # {1, 2, 5, 6}         symmetric difference

# Membership test — O(1)
"python" in tags    # True

# Deduplication
unique = list(set([1, 2, 2, 3, 3, 3]))   # [1, 2, 3]

tuple

Tuples are immutable sequences — use them for records and as dict keys.

point = (3.0, 4.0)
rgb = (255, 128, 0)

# Unpacking
x, y = point
r, g, b = rgb

# Extended unpacking
first, *rest = (1, 2, 3, 4, 5)   # first=1, rest=[2,3,4,5]
*init, last = (1, 2, 3, 4, 5)    # init=[1,2,3,4], last=5

# Tuple as dict key (lists cannot be)
cache = {}
cache[(0, 0)] = "origin"

# Single-element tuple needs trailing comma
one = (42,)   # not (42) which is just int 42

collections.deque

A double-ended queue — O(1) appends and pops from both ends. Use instead of list for queues.

from collections import deque

q = deque(maxlen=3)   # fixed-size circular buffer
q.append(1)
q.append(2)
q.append(3)
q.append(4)   # 1 is evicted: deque([2, 3, 4])

q.appendleft(0)   # prepend
q.popleft()       # O(1) — unlike list.pop(0) which is O(n)

# BFS queue
from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.add(node)
            queue.extend(graph[node])
    return visited

collections.defaultdict

Like dict, but returns a default value for missing keys instead of raising KeyError.

from collections import defaultdict

# Group items by key
words = ["apple", "ant", "banana", "avocado", "blueberry"]
by_letter = defaultdict(list)

for word in words:
    by_letter[word[0]].append(word)

# {"a": ["apple", "ant", "avocado"], "b": ["banana", "blueberry"]}

# Count occurrences
freq = defaultdict(int)
for char in "mississippi":
    freq[char] += 1

# Nested defaultdict
nested = defaultdict(lambda: defaultdict(int))
nested["users"]["alice"] += 1

collections.Counter

A specialized dict for counting hashable objects.

from collections import Counter

words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
count = Counter(words)
# Counter({"apple": 3, "banana": 2, "cherry": 1})

count.most_common(2)   # [("apple", 3), ("banana", 2)]
count["apple"]         # 3
count["grape"]         # 0  (no KeyError)

# Arithmetic
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
c1 + c2   # Counter(a=4, b=3)
c1 - c2   # Counter(a=2)  (negative counts removed)

# Count characters
letter_freq = Counter("hello world")

collections.namedtuple

Tuples with named fields — great for lightweight data records.

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3.0, 4.0)

p.x        # 3.0
p.y        # 4.0
p[0]       # 3.0  (indexing still works)
x, y = p   # unpacking works

# Immutable — cannot change fields
# p.x = 5   raises AttributeError

# Convert to dict
p._asdict()   # {"x": 3.0, "y": 4.0}

# Replace fields
p2 = p._replace(x=10.0)   # Point(x=10.0, y=4.0)

For mutable records with type hints, prefer dataclasses.dataclass instead.

Choosing the Right Collection

NeedUse
Ordered sequence, mutablelist
Ordered sequence, immutabletuple
Key-value mappingdict
Unique elements, fast membershipset
Fast queue (both ends)deque
CountingCounter
Group-by / missing key defaultdefaultdict
Lightweight record with namesnamedtuple or dataclass

Frequently Asked Questions

When should I use a tuple instead of a list?
Use tuples for fixed, heterogeneous data (a coordinate, a record) and lists for homogeneous, variable-length sequences. Tuples are also hashable so they can be dict keys or set members.
What's the time complexity of dict lookups?
O(1) average case. Python dicts are hash tables. Lookups, insertions, and deletions are all O(1) on average.
What makes a set element or dict key valid?
It must be hashable — immutable built-in types (int, str, tuple of hashables) are hashable. Lists and dicts are not.