I have seen that in Python 3 there are objects of type iterable , objects of type iterator and there are also sequences . My questions are:
What is the difference? Can they all be traversed in a loop? Are all of them accessible by indexes? Do they all contain elements in memory or are they generated interactively?
This is the explanation to each of the types to clarify the difference:
Iterator : An iterator type object is an object that represents a stream of data, which can be traversed in an iterative process, such as a loop
for
, within a functionmap
orfilter
, in the creation of a list comprehension or generator , or in a comparisonin
.Every iterator object contains an implemented method
__next__()
that is called on each iteration, returning the successive elements of the data stream each time. The data flow of the object does not have to be stored in memory, but can be generated in real time in each iteration.The iterator object saves an internal state to know what was the last element obtained. Thus, on the next call to
__next__()
, the next correct element will be returned.When there are no more elements left in the iterator 's data stream , the function
__next__()
firesStopIteration
. The internal state is not automatically reset when reaching the end of the stream or starting to loop through it again. That is, it can only be traversed once.Also, it has implemented the method
__iter__()
that returns the iterator object itself . 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. It has implemented one of these two methods:
__iter__()
which returns an iterator object from this iterable object .__getitem__()
which accesses each of the elements for indices starting from 0.An iterable object does not have to have the method defined
__next__()
. Instead, having the obligation to implement the method__iter__()
or__getitem__()
, it can be used as an argument for the functioniter()
and thus iterate through the resulting iterator .Sequence : It is a collection object that has the method defined
__getitem__()
(in addition to__setitem__()
or__delitem__()
if the sequence is mutable) to access the elements of the sequence. The value passed as argument to__getitem__()
a sequence is either an index or a slicing object .There is another type of collection object called a mapper (mapping) that also has the method defined
__getitem__()
, with the difference that the value passed by argument is a unique key. Also, like sequences , if the mapper__setitem__()
is mutable it has the or methods defined__delitem__()
.The function
__getitem__()
is executed implicitly by using the operator[]
with the access argument value enclosed in square brackets.Both sequences and mappers
__len__()
have e methods defined__iter__()
as they are both collections . The first allows the length of the elements of the collection to be returned when it is passed as an argument to the functionlen()
. The second is because collections are iterable .Lists and tuples are examples of sequence collections that are also iterable by having the
__iter__()
and methods implemented__getitem__()
(for indexes starting from 0). But instead they are not iterator because they do not contain the method__next__()
The function
iter()
, which is part of the Python language, returns an iterator from an object containing the method__iter__()
, or containing the method__getitem__()
for indices starting from 0 (throwingIndexError
if the index does not exist). If it doesn't contain either of these two methods, then the functioniter()
castsTypeError
instead of returning an iterator from the object.The function
iter()
is always called implicitly at the start of an iteration as a loopfor
. This function is passed the iterable object on which you want to iterate, returning a new iterator that will be traversed. For this reason, iterator objects must have a method__iter__()
: so that the functioniter()
, always executed on the object to be iterated before starting the iteration, returns the iterator itself (without resetting its state__next__()
on each call toiter()
).The function
iter()
can also generate an iterator from a function (object that has the method implemented__call__()
) instead of from an iterable object . To do this, the first argument is passed toiter()
the function that will be called without arguments in each__next()__
, and the completion value is passed as the second argument. The iterator will stop giving elements by throwingStopIteration
when the function, called on each iteration__next()__
, returns the value of completion.Lastly, as much
Iterable
as theyIterator
are abstract classes defined within the modulecollections.abc
which contain the abstract methods__iter__()
or__next__()
. Therefore, an instance of a class inheriting fromIterable
must implement the method__iter__()
(by satisfying one of the above conditions for iterables ); and an instance of a class inherited fromIterator
must implement the methods__iter__()
and satisfy the iterator__next__()
conditions explained above.