Iterators, iterables, and generators
In Python, we frequently encounter iterators, iterables, and generators as these are efficient ways of looping over a data type or data structure that is a sequence or a sequence can be created out of it. One clear advantage of using these looping techniques is that they require less memory. So, when you must access a sequence element by element, these techniques become very useful because a large sequence does not need to be loaded into memory all at once. For example, if you need to find the square of the first one trillion positive integers, there is no need to create a data structure to hold all numbers in memory at the same time. Iterators, iterables, and generators can be used to generate and process these numbers sequentially. Another example is processing a large text file. The entire file might not fit in memory. Hence, if we need to process the file, for example, to find word count per line of the file, we can iteratively loop over the lines...