Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of yield with a dict comprehension

Tags:

As a contrived example:

myset = set(['a', 'b', 'c', 'd']) mydict = {item: (yield ''.join([item, 's'])) for item in myset} 

and list(mydict) gives:

['as', 'cs', 'bs', 'ds', {'a': None, 'b': None, 'c': None, 'd': None}] 

What happens here? What does yield do? And is this behavior consistent no matter what expression follows yield?

Note: I know that doing mydict = {item: ''.join([item, 's']) for item in myset} would give the dictionary {'a': 'as', 'b': 'bs', 'c': 'cs', 'd': 'ds'}, which seems to be what I am trying to do here.

like image 345
skyork Avatar asked Sep 10 '12 19:09

skyork


1 Answers

First of all, what does yield return? The answer in this case is None, because yield returns the parameter passed to next(), which is nothing in this case (list doesn't pass anything to next).

Now here's your answer:

>>> myset = set(['a', 'b', 'c', 'd']) >>> mydict = {item: (yield ''.join([item, 's'])) for item in myset} >>> mydict <generator object <dictcomp> at 0x0222BB20> 

The dict comprehension is turned into a generator, because you used yield in a function body context! This means that the whole thing isn't evaluated until it's passed into list.

So here's what happens:

  1. list calls next(mydict).
  2. Yield returns ''.join([item, 's']) to list and freezes the comprehension.
  3. list calls next(mydict).
  4. The comprehension resumes and assigns the result of yield (None) to item in the dictionary and starts a new comprehension iteration.
  5. Go back to 1.

And at last the actual generator object returns the temporary in the body, which was the dict. Why this happens is unknown to me, and it's probably not documented behaviour either.

like image 62
orlp Avatar answered Sep 23 '22 02:09

orlp