2 (most?) common uses of generators:
- async control flow
- this is what the js community has mostly been talking about
- that's a huge topic, and it's better covered elsewhere, so i'm not going to go over that stuff here
- iteration - this is probably the most common use case for python
i've got a comparison of iteration techniques between generators (how python does it), and callbacks (currently the preferred technique in js). they look kinda similar. take a minute to study both.
all done? ok here's the similarities and differences i see:
-
the internal state of iteration is abstracted
- i.e. the "our code" sections don't have to understand how odd numbers work
- so both are an improvement over the C-style
for
loops
-
they both enable efficiently iterating over huge collections
- odd numbers are kind of trivial, but you can see how these could be used for incrementally processing a big file or something
-
it's just a function
- everybody knows how they work
- they make great building blocks for libs like underscore
-
provides a new scope so you can have variables local to the inside of the loop
- you avoid problems like this
- es6
let
statements accomplish this, so this won't be as important in the future
-
this
changes, so instance methods like the one below require extra boilerplate (contrived example):Model.prototype.values = function() { var values = []; this._properties.forEach(function(prop) { // DOH! "this" is no longer our Model instance values.push(this.get(prop)); }); return values; };
-
you can't
break
out of the loop early -
stepping through it in a debugger isn't as easy
- you have to either step into the
forEach
method (not even possible if it's natively supported) - or set a breakpoint in the callback
- you have to either step into the
-
callbacks don't compose
-
with generators, you can consume the output of one in another
def factors_of_three(gen): for x in gen: yield x % 3 == 0 # odd factors of three for x in factors_of_three(odd_numbers)): print x
-
also consider filtering a huge array, then mapping the values:
- using underscore,
_.map(_.filter(arr, myCondition), myTransform)
, you create two copies of the array (albeit they're smaller since they're filtered) - using python generators
(my_transform(x) for x in arr if my_condition(x))
you haven't created any copies
- using underscore,
-
part of the reason generators are so handy in python is that it has great language support. python's for...in
and list comprehensions understand how to consume generators, where as for...in
in javascript does not. luckily the es6 standard that defines generators also has for...of
and array comprehensions that work with generators, but the tricky thing is we don't know when browsers will roll out support for them.