Starting up with the last of the Lerner courses, I got.
Iterators and Generators.
Hopefully I get done with this and use the same intensity with actually writing code.
What shape will that take and how will I write about it? I have no clue
For now, notes follow.
Notes
** Part 1, Iterators **
- If we run the
iterfunction on something and it comes back with an iterator object, then it’s iterable; I can iterate on it. - Behind scenes in a
forloop, Python does something similar. It asks if the object that has to be looped over is iterable withiter. And then keeps doingnextwith it to get the next result/item, until it comes across aStopIterationwhich lets it know, that the values are exhausted and it can stop looping. - The
forloop is a very thin shell, that asks the appropriate questions of the iterator. But it is always the iterator that determines, what exactly is returned. - iterable vs iterator
- iterable means that it can be put into a “for” loop in Python
- things like strings, lists, dictionaries, sets, files and objects (that I will soon create) that implement that iterator protocol.
- it responds positively to the
iterfunction’s question, Are you iterable? It returns an iterator object instead of raising errors (TypeErrors in this case)
- an iterator is the object returned by the iterable, the thing on which the
nextfunction is run.- it could be the original object (the iterable) itself.
- it could also be a seperate object returned by the iterable.
- when the iterator object is exhausted, it returns
StopIteration - these are what give us the values.
- iterable means that it can be put into a “for” loop in Python
- How do I make my objects iterable?
- By implementing the iterator protocol
- the object must respond to iter, by returning an iterator object
- it should respond to the next function with values
- it should raise a
StopIterationerror, when it’s done.
- By implementing the iterator protocol
- Best to create a helper class, pass the data to it and return that as the iterator instance.
- helps with state, since I can then use the same object and iterate over it multiple times independantly.
** Part 2, Generators **
- Creating a whole class just to implement an iterator, might be overkill
- Generators are a solution, that might come in handy.
- They are like functions; functions that implement the iterator protocol.
- instead of using
return, we useyield.- this lets us return multiple items.
- I can use them to chunk large items, buffer uncertain stuff, and wrap complex stuff.
- Rule of thumb,
- if I already have a class, just added functionality to it using an iterator class.
- but if I need something standalone, just use a generator function
** Part 3, Coroutines **
- Little helper functions.
- With such a function, I assign the
yieldstatment to a variable. - This makes the function sit and wait for me to
sendit something. - Once it receives data, it ingests it, processes it and poops out, yields an answer :)
x = yield x- Instead of sending data back to a coroutine with
send, I could send it an exception usingthrow. - I use
closeto close the generator and break out (trapGeneratorExit) - I can just say
yield from some_generatorif I am calling another generator from my generator.
- With such a function, I assign the
** Part 4, Generator Comprehensions **
- Create one just using ()
genvar = (x*x for x in range(-5,5))will make genvar a generator object. - The general idea, I am getting, behind the idea of generators and iterators is that we use them on really large objects, so that we don’t run out of memory.
- for e.g. joining a list of 3 elements is easy-peasy. A list of 3 billion elements less so, and ergo, generators and iterators.
- the ultimate idea being, just like an oil pipeline. We ought not to store it in tanks (lists, dictionaries etc.) We ought to strive as much as possible that it flows from upstream, right through to our taps in a single unbroken stream. Just pass the oil along, using various t-junctions and pipes and motors and stuff. Keep it flowing. Minimise storage.
- it also spreads wait times across well, time. So I never have to wait for something to gather up and collect and then display. the generator comes back instantly and when it has to get a values, I just have to wait for that one value.
** Part 5, itertools **
- itertools is a collection of generators/iterators, that’ll let me add the iterator protocol to existing classes and functions. Looking through these feels like looking at specialty carpentry tools, like this biscuit joiner.
- lets me take advantage of other smarter folks’ work and optimisations
itertools.chaincrawls elements across iterablesitertools.countcounts upwards from a number given, in steps if we want.itertools.cyclecycles through a range of optionsitertools.repeatrepeats a value for the number of times, we tell ititertools.filterfalsetakes a iterable and a function, applies the function to the iterable and if the function returns false, only then is the element from the iterable, passed through. (the inverse in which only true elements are returned is a built-in in Python, aptly calledfilter)itertools.takewhileanditertools.dropwhileare opposites of each other- takewhile takes a function and a sequence, applies the function to the sequence (starts a
whileloop) and then immediately stops if it hits false - dropwhile inverts that. it starts a
whileloop when it hits it’s first false and then keeps going from there.
- takewhile takes a function and a sequence, applies the function to the sequence (starts a
itertools.compresstakes two iterables of equal length, (or if they are unequal, it’ll stop when it reaches the end of the shorter one) and then if the element second iterable rings true, it returns the element in the first one.itertools.accumulateadds the first element to the second element, then adds that the third element and so on. (if i want to use some other function of add, I can do that too)itertools.teelets me create multiple instances of a generator, each with it’s own state (memory intensive.)- permutations and combinations
itertools.producttakes an element from one iterable, runs it across all the elements of the second (or third and fourth) and returns a cartesian product, an x,y (or x,y,z) coordinate tuple.itertools.permutationsdoes permutations across two iterables. returns tuples.itertools.combinationsdoes combinations.itertools.combinations_with_replacementsdoes combinations allowing each element to repeat. (a combination might not allow (10, 10, 10) for example; this helps with that)
- more-itertools is an external package that gives me more complex itertools. (
pip install more-itertools) need to look at them later.
Learning / Feedback/ Experiences from doing exercises
- Reuven tells stories “Deep in the recesses of Python …”
- Reuven’s talking Python interpreter, “Hey Object, are you iterable?”
- I am slowly anticipating the problems in Reuven’s code as he keeps iterating on them.
- ‘Will this work?’, asks Reuven.
‘NO!’, I yell back at the screen. ‘This function does not do anything, will return None and cause an error!’
I am learning! This is fun! :)
- ‘Will this work?’, asks Reuven.
- Been going, “Ooooh! So that’s how they do it!” lots of times in this class.
- Ok, have now dropped from a 30,000 foot view of Python to a 10,000 foot view. Will hit ground level, starting tomorrow, when I start doing exercises.
Read all about my Python Iterators & Generators journey here