An object is called an iterable object if it has an __iter__ method. List, tuple, dict are all iterable objects[reference]:
The __iter__ method returns an iterator object. An object is called an iterator if it implements the __next__ method and the __iter__ method:
From the figs. above, we can see an iterator is also a iterable object because it has the __iter__ method. But an iterable object is not necessarily an iterator. The __next__ method returns the next item whenever it is called:
We do not need to call the __iter__ method of an iterable object to get its iterator, we can use the iter() function. We do not need to call the __next__ method of an iterator, we can use the next() function[reference].
In the for…in… statement, the object after the in keyword must be an iterable object. The for statement gets its iterator and call the next statement for the iterator in every loop[reference].
enumerate is an iterator:
So, enumerate can also be used in for…in.. statement. The __next__ method of this iterator returns a tuple. The first value of this tuple is an index. You can convert the tuple to an list using the builtin list function:
The range function returns an iterable object:
So range can be used in the for..in… statement.
Generator is also an iterator:
The generator function (the function that contains yield in its body) returns a generator object immediately. Since the generator object has __iter__ and __next__ methods, it is an iterator and can be used in for…in… statement.[reference]