This is the explanation to each of the types to clarify the difference:
-
Iterator : An iterator type object is an object that represents a flow of data, which can be traversed in an iterative process, such as a loop for
, within a function map
or filter
, in the creation of a list comprehension or generator , or in a comparison in
.
Every iterator object contains a __next__()
method that is called in each iteration by returning the successive elements of the data flow each time. The data flow of the object does not have to be stored in memory, but it can be generated in real time in each iteration.
The iterator object saves an internal state to know which was the last element obtained. So, in the next call to __next__()
, you will get the next correct element.
When there are no more items left in the iterator data stream, the __next__()
function throws StopIteration
. The internal state does not restart automatically when you reach the end of the flow or when you start traversing it again. That is, you can only go once.
In addition, you have implemented the __iter__()
method that the iterator object itself returns. This is necessary to be able to implement loops with iterator objects, as we will explain later.
-
Iterable : An iterable object is a type of object that returns its elements one at a time at a time. You have implemented one of these two methods:
-
__iter__()
that returns an iterator object from this iterable object.
-
__getitem__()
that accesses each of the elements for indexes starting from 0.
An iterable object does not have to have the __next__()
method defined, however, when having the obligation to implement the __iter__()
or __getitem__()
method, it can be used as an argument to the function iter()
and so traverse the resulting iterator .
-
Container : A container is an object that has the __getitem__()
method defined (in addition to __setitem__()
or __delitem__()
if the container is mutable) to access the container elements. Depending on whether the container is a sequence or a mapping, the value passed by argument to __getitem__()
can be an index or a slicing object for the first, or a unique key for the second.
The __getitem__()
function is implicitly executed when using the []
operator with the access argument value in brackets.
The lists and tuples are sequence containers which are also iterable when the __iter__()
method is implemented and the __getitem__()
method (for indexes starting from 0). But instead they are not iterator because they do not contain the __next__()
method
The function iter()
, which is part of the Pyhton language, returns an iterator from an object that contains the __iter__()
method, or that contains the __getitem__()
method for indexes starting from 0 (throwing IndexError
if the index does not exist). If it does not contain either of these two methods, then the iter()
function throws TypeError
instead of returning an iterator from the object.
The iter()
function is always implicitly called at the start of an iteration as a for
loop. This function is passed the object on which you want to iterate, returning an iterator which will be traversed. For this reason the iterator objects must have a __iter__()
method: for the iter()
function, always executed on the object to be iterated before starting the iteration, return an iterator .
[The iter()
function can also generate an iterator from a function (object that has the __call__()
method implemented) instead of from an object].
Finally, both Iterable
and Iterator
are abstract classes defined within the module collections
which contain the abstract methods __iter__()
or __next__()
. Therefore, an instance of a class inherited from Iterable
must implement the __iter__()
method (making it meet one of the above conditions for iterable ); and an instance of a class inherited from Iterator
must implement the methods __iter__()
and __next__()
fulfilling the conditions of iterator explained above.