Course Content
Python Essentials for AI Engineer
6 sections · 48 lessons
What is the difference between iterable and iterator?
What you need to know
The protocol by hand
1docs = ["doc1", "doc2"] # an ITERABLE2it = iter(docs) # ask it for an ITERATOR3print(next(it)) # doc14print(next(it)) # doc25try:6 next(it)7except StopIteration:8 print("exhausted") # exhausted9print(iter(it) is it) # True -> an iterator is its own iteratorA for loop does exactly this: it calls iter(docs), calls next() until StopIteration, and hides the exception.
Exhaustion
gen = (x * x for x in range(3)) # a generator is an iteratorprint(list(gen)) # [0, 1, 4]print(list(gen)) # [] -> already used up, no errorGenerators: iterators you write with yield
A function containing yield returns a generator. Each next() runs the function until the next yield, hands out that value, and pauses with all local variables kept. Nothing is computed until someone asks, so a generator can describe a million rows while holding only one.
A real-life example
You must embed a 5 GB JSONL file (one JSON document per line) with an API that accepts at most 64 texts per request. Loading the file into a list would need more RAM than the container has. Two small generators stream it instead:
1import io, json23def read_jsonl(f):4 for line in f: # a file object is an iterator of lines5 if line.strip():6 yield json.loads(line)["text"]78def batched(items, size):9 batch = []10 for item in items:11 batch.append(item)12 if len(batch) == size:13 yield batch14 batch = []15 if batch:16 yield batch # the last, smaller batch1718f = io.StringIO("".join(json.dumps({"text": f"doc {i}"}) + "\n" for i in range(150)))19for batch in batched(read_jsonl(f), 64):20 print(len(batch), batch[0]) # embed(batch) would go here21# 64 doc 022# 64 doc 6423# 22 doc 128At any moment only one batch of 64 texts is in memory, whether the file has 150 lines or 150 million. Python 3.12 added itertools.batched, which does the same job (it yields tuples); the hand-written version above works on 3.11 too.
Follow-up questions to expect
- "Is a list an iterator?" — No. It is an iterable:
next([1, 2])raisesTypeError.iter([1, 2])gives an iterator. - "Why can I loop over a list twice but not a generator?" — Each loop over a list creates a new iterator; a generator is the iterator, and once exhausted it stays empty.
- "What is the difference between
yieldandreturn?" —returnends the function;yieldhands out one value and pauses, so the function can continue on the nextnext()call.