After reading answer1 and answer2, purpose of yield
still looks unclear.
In this first case, with the below function,
def createGenerator():
mylist = range(3)
for i in mylist:
yield i*i
On invoking createGenerator
, below,
myGenerator = createGenerator()
should return object(like (x*x for x in range(3))
) of type collections.abc.Generator
type, is-a collections.abc.Iterator
& collections.abc.Iterable
To iterate over myGenerator
object and get first value(0
),
next(myGenerator)
would actually make for
loop of createGenerator
function to internally invoke __iter__(myGenerator)
and retrieve collections.abc.Iterator
type object( obj
(say) ) and then invoke __next__(obj)
to get first value(0
) followed by the pause of for
loop using yield
keyword
If this understanding(above) is correct, then,
then, does the below syntax(second case),
def createGenerator():
return (x*x for x in range(3))
myGen = createGenerator() # returns collections.abc.Generator type object
next(myGen) # next() must internally invoke __next__(__iter__(myGen)) to provide first value(0) and no need to pause
wouldn't suffice to serve the same purpose(above) and looks more readable? Aren't both syntax memory efficient? If yes, then, when should I use yield
keyword? Is there a case, where yield
could be a must use?
Try doing this without yield
def func():
x = 1
while 1:
y = yield x
x += y
f = func()
f.next() # Returns 1
f.send(3) # returns 4
f.send(10) # returns 14
The generator has two important features:
The generator some state (the value of x
). Because of this state, this generator could eventually return any number of results without using huge amounts of memory.
Because of the state and the yield
, we can provide the generator with information that it uses to compute its next output. That value is assigned to y
when we call send
.
I don't think this is possible without yield
.
That said, I'm pretty sure that anything you can do with a generator function can also be done with a class.
Here's an example of a class that does exactly the same thing (python 2 syntax):
class MyGenerator(object):
def __init__(self):
self.x = 1
def next(self):
return self.x
def send(self, y):
self.x += y
return self.next()
I didn't implement __iter__
but it's pretty obvious how that should work.