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?