Analogy
Imagine that you have created a remarkable machine that is capable of generating thousands and thousands of lightbulbs per day. The machine generates these lightbulbs in boxes with a unique serial number. You don't have enough space to store all of these lightbulbs at the same time (i.e., you cannot keep up due to storage limitation), so you would like to adjust it to generate lightbulbs on demand-demand.
Python generators don't differ much from this concept. Imagine that you have a function called barcode_generator
that generates unique serial numbers for the boxes. Obviously, you can have a huge number of such barcodes generatedreturned by the function, subject to the hardware (RAM) limitations. A wiser, and space efficient, option is to generate those serial numbers on-demand.
Note the next(barcode)
bit.
As you can see, we have a self-contained “function” to generate the next unique serial number each time. This function returns a generator! As you can see, we are not calling the function each time we need a new serial number, but instead we are using next()
given the generator to obtain the next serial number.
Output:
How many lightbulbs to generate? 5
[10000, 10001, 10002, 10003, 10004]
Produce more? [Y/n]: y
How many lightbulbs to generate? 6
[10005, 10006, 10007, 10008, 10009, 10010]
Produce more? [Y/n]: y
How many lightbulbs to generate? 7
[10011, 10012, 10013, 10014, 10015, 10016, 10017]
Produce more? [Y/n]: n
To be more precise, this generator is a lazy iterator! These lazy iterators doAn iterator is an object that helps us traverse a sequence of objects. It's called lazy because it does not store their contentload all the items of the sequence in memory! until they are needed. The use of next
in the previous example is the explicitexplicit way of obtainingto obtain the next item from the iterator. The implicitimplicit way is using for loops:
These lazy iteratorsIn other words, a generator looks like a function but behaves like an iterator.
Real-world application?
Finally, real-world applications? They are usually useful when you work with big sequences (for example,. Imagine reading a reading large fileshuge file from disk with billions of records. Reading the entire file in memory, before you can work with its content, will probably be infeasible (i.e., you will run out of memory).