Python's built-in data structures are flexible enough to cover most needs, but picking the right one matters a lot for both clarity and performance.
Lists: Ordered and Mutable
fruits = ["apple", "banana", "cherry"]
fruits.append("date")
fruits[0] = "avocado"
Lists are ideal when you need an ordered, changeable collection. Indexing and appending are fast (O(1) amortized), but searching for a value or inserting at the front is slow (O(n)).
Tuples: Ordered and Immutable
point = (3, 4)
x, y = point # unpacking
Tuples behave like lists but can't be modified after creation. This makes them useful as dictionary keys, function return values representing fixed structures, and anywhere you want to signal "this shouldn't change."
Dictionaries: Key-Value Mapping
user = {"name": "Alice", "age": 30}
user["email"] = "alice@example.com"
print(user.get("phone", "not provided"))
Dictionaries offer average O(1) lookup, insertion, and deletion by key, implemented via hash tables. Since Python 3.7, dictionaries also preserve insertion order.
Sets: Unique, Unordered Collections
a = {1, 2, 3}
b = {2, 3, 4}
print(a | b) # union: {1, 2, 3, 4}
print(a & b) # intersection: {2, 3}
print(a - b) # difference: {1}
Sets automatically eliminate duplicates and offer O(1) membership testing (x in a), far faster than checking membership in a list for large collections.
Choosing Between Them: A Quick Guide
| Need | Use |
|---|---|
| Ordered, changeable sequence | list |
| Ordered, fixed sequence | tuple |
| Fast key-based lookup | dict |
| Unique items, fast membership test | set |
Beyond the Basics: collections Module
defaultdict — avoids KeyError by supplying a default value:
from collections import defaultdict
counts = defaultdict(int)
for word in ["a", "b", "a", "c", "b", "a"]:
counts[word] += 1
Counter — purpose-built for counting:
from collections import Counter
counts = Counter(["a", "b", "a", "c", "b", "a"])
print(counts.most_common(2)) # [('a', 3), ('b', 2)]
deque — a double-ended queue with O(1) appends/pops from both ends, unlike lists which are slow at the front:
from collections import deque
queue = deque([1, 2, 3])
queue.appendleft(0)
queue.append(4)
namedtuple — lightweight, immutable objects with named fields:
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y) # 3 4
Performance Matters
A common beginner mistake is using a list where membership testing happens repeatedly:
# Slow: O(n) per lookup
allowed = ["admin", "editor", "viewer"]
if role in allowed: # ...
# Fast: O(1) per lookup
allowed = {"admin", "editor", "viewer"}
if role in allowed: # ...
For small collections the difference is negligible, but at scale it adds up quickly.
Key Takeaway
Python's core data structures each optimize for different access patterns: lists for order and mutation, tuples for fixed sequences, dicts for lookup by key, and sets for uniqueness and fast membership tests. Picking the right one is often the single biggest factor in both code clarity and performance.
Comments (0)
No comments yet
Be the first to share your thoughts!