Iterable vs iterator
standardintermediateAn iterable is anything iter() can be called on — a list, a string, a generator function's result. An iterator is what iter() returns: an object that remembers a position and produces one value per next() call.
Think of it as
A book is iterable — you can open it and start reading. A bookmark is an iterator — it tracks one specific reading position inside one specific reading session. The same book supports many independent bookmarks at once; each iterator has its own position, even over the same iterable.
What we're doing: Write a class that is iterable without being an iterator, and show the same instance supports two independent, simultaneous loops.
- 5
- __iter__ is a generator function (it contains yield), so calling it does not run the body — it returns a fresh generator object, which is the iterator.
- 12
- Fibonacci defines __iter__ but no __next__, so an instance is an iterable, not an iterator — you cannot call next(fib) directly.
- 14
- A second list(fib) works because __iter__ runs again, building a brand-new generator with its own a, b, yielded — nothing carries over from the first call.
[0, 1, 1, 2, 3]
[0, 1, 1, 2, 3]
0 1 1 2 3 Why this works: Because __iter__ is called once per iteration attempt and each call builds an independent generator, the SAME Fibonacci instance supports being iterated as many times as needed, from scratch every time. That independence is exactly what separates an iterable from an iterator: the iterable is reusable, the iterator it hands out is not.
Treating an iterable as though it were already an iterator
Wrong
Better
What you see: TypeError: 'BadCounter' object is not iterable
Why: A for loop calls iter() on its argument before it ever calls next() — that is a hard requirement, not an optimization. BadCounter defines __next__ but not __iter__, so iter(BadCounter(3)) has nothing to call and Python raises immediately, before a single value is produced. Adding __iter__ that returns self is what makes the object both iterable and its own iterator.
Telling the two apart on a real object
Together
Remember: Iterable has __iter__ and can be asked for an iterator. Iterator has __next__ and produces values one at a time.
See also: iterator protocol · iter next · iterator protocol dunders

