In this, hopefully, very short write-up I will discuss a few facts a Pythonista should know about...
When I started with Python, I used those words interchangeably because they are so similar. Without any technical details, allow me to share my simplistic guide how to decide which is which.
But first of all, we will prepare the stage. See how our contestants enter proudly.
iterable = [1,2,3] iterator = iter(iterable)
For future reference, remember I used a list of three integers as iterable and created my iterator from this list. In next text, I will call them accordingly and explain
If the object in question do not raise an exception after the next function is applied, call it an iterator.
# The iterator won't complain assert next(iterator) == 1
On the other hand, the following code will fail. Call it iterable if, of course, the iter function works on it. In practice, the term iterable depicts objects from which one can obtain an iterator.
Let's make some tests.
try: next(iterable) except TypeError: print("prints: The type list is clearly not an iterator!") try: iterable.__next__ except AttributeError: print("prints: No __next__ method implemented!") iterable.__iter__ print("prints: No complains here as this iterable implements __iter__ method!")
prints: The type list is clearly not an iterator! prints: No __next__ method implemented! prints: No complains here as this iterable implements __iter__ method!
To put it simply, it is just a question of two dunder methods and their implementations:
def __next__(self) def __iter__(self)
I don't intend to explain what the methods do. You may ask in the comment section if you need more info. Suffice it to say that these are equivalent:
next(Something) equals to Something.__next__() iter(Something) equals to Something.__iter__()
In this particular case, I created the iterator from a list, which is mutable by default. Our two inspected objects (iterable and iterator) are linked closely. If the first, understand the iterable changes, the latter will follow its lead. Behold!
iterable = "changed" assert next(iterator) == "changed"
The curious mind may wonder what happens if we destroy the original object, the iterable. If they both share a link, deleting first one should invalidate the latter one, the iterator.
In reality, even removing the iterable from our namespace doesn't damage the bound these two share. Being once again technical, the garbage collector cannot remove the iterable since there is one lasting reference to it, held by the iterator.
Just check it!
assert next(iterator) == 3
We had three numbers in our iterable, so the iterator has to be exhausted by now. As there is nothing to pull, Python raises the StopIteration exception to indicate we ask too much.
This is a crucial part to understand. After the iteration is over, the typical iterator doesn't start from the beginning. The story ends here.
try: next(iterator) except StopIteration: print("prints: THE END!")
prints: THE END!