This episode deepens Python's collection data types: the collections module with deque, defaultdict, Counter, and namedtuple, plus the basics of typing for type annotations. You'll also learn memory and performance considerations when choosing data structures.

We already know list, dict, set, and tuple. But Python has the collections module that provides specialized versions with better behavior and performance for specific cases. Episode 5 covers them one by one.
We also start touching typing — type annotations that are becoming increasingly important in modern Python and that we'll deepen in episode 17. Finally, we discuss memory and performance considerations so you can choose the right data structure.
deque is a double-ended queue with O(1) append and pop operations at both ends:
from collections import deque
antrian = deque(["a", "b", "c"])
antrian.append("d")
antrian.appendleft("z")
print(antrian)
item = antrian.popleft()
print(item)
print(antrian)deque(["a", "b", "c"]) creates a double-ended queue. Unlike a list, whose insert(0, ...) is slow, deque.appendleft and popleft are always fast. deque is a great fit for queues, buffers, and sliding windows.
defaultdict provides automatic default values when a key doesn't exist, removing the boilerplate of checking:
from collections import defaultdict
jumlah = defaultdict(int)
kata = ["a", "b", "a", "c", "a"]
for k in kata:
jumlah[k] += 1
print(dict(jumlah))
print(jumlah["tidak_ada"])defaultdict(int) creates a dict that calls int() when a key isn't found, so jumlah[k] += 1 works directly without checks. Other variants can use list, set, or a custom function.
Counter is a specialized dict for counting element occurrences:
from collections import Counter
kata = ["apel", "jeruk", "apel", "mangga", "apel"]
hitung = Counter(kata)
print(hitung)
print(hitung.most_common(2))Counter(kata) counts the frequency of each element. hitung.most_common(2) returns the two most frequent elements. Counter supports arithmetic operations like adding and subtracting between Counters.
namedtuple creates a tuple whose fields can be accessed by name:
from collections import namedtuple
Titik = namedtuple("Titik", ["x", "y"])
t = Titik(3, 4)
print(t.x)
print(t.y)
print(t[0])namedtuple("Titik", ["x", "y"]) creates a tuple class with named fields. t.x and t[0] return the same value. namedtuple makes code more descriptive while staying as memory-efficient as a tuple.
The typing module provides generic types for annotations. Annotations make code clearer and can be checked by mypy:
from typing import List, Dict, Optional
def rata_rata(angka: List[float]) -> float:
return sum(angka) / len(angka)
def cari_nama(data: Dict[str, str], kunci: str) -> Optional[str]:
return data.get(kunci)
print(rata_rata([1.0, 2.0, 3.0]))
print(cari_nama({"a": "Arman"}, "a"))List[float] declares a list of floats, Dict[str, str] maps strings to strings, and Optional[str] can hold a string or None. Annotations don't affect runtime, but they serve as documentation and input for mypy validation in episode 17.
For more complex cases, Python 3.10 and above provide the X | Y syntax:
from typing import Union
def tampilkan(nilai: Union[int, str]) -> str:
return str(nilai)
def tampilkan_py310(nilai: int | str) -> str:
return str(nilai)
print(tampilkan(10))
print(tampilkan_py310("halo"))int | str in Python 3.10 replaces Union[int, str]. This pipe syntax is more concise and has become the standard in modern code. We'll cover Annotated, TypedDict, Protocol, and ParamSpec in episode 17.
Every data structure has memory and speed trade-offs:
Let's compare the lookup speed of a list and a set with a large dataset:
import time
data = list(range(100_000))
target = 99_999
mulai = time.perf_counter()
ada_list = target in data
lama_list = time.perf_counter() - mulai
himpunan = set(data)
mulai = time.perf_counter()
ada_set = target in himpunan
lama_set = time.perf_counter() - mulai
print(ada_list, lama_list)
print(ada_set, lama_set)target in himpunan uses a hash table so the lookup is nearly instant, while target in data on a list scans linearly. For repeated membership checks, a set always wins.
Key takeaways:
In the next episode, episode 6, we'll cover exception handling and resource management — try, except, else, and finally, creating custom exceptions, context managers with the with keyword, and contextlib for building your own context managers. These are essential skills to make your programs resilient to errors!