As a contrived example:
myset = set([\'a\', \'b\', \'c\', \'d\'])
mydict = {item: (yield \'\'.join([item, \'s\'])) for item in myset}
and
I think that your code has to execute similarity of this:
def d(myset):
for item in myset:
yield item, (yield ''.join([item, 's']))
d(myset)
Firstly, evaluated yield ''.join([item, 's']
(and return 'as', 'cs', etc.). Value of yield expression is None, because is sent back to the generator. And then eval yield item, None
, that return tuples ('a', None), ('b', None).
So, I have:
>>> list(d(myset))
['as', ('a', None), 'cs', ('c', None), 'bs', ('b', None), 'ds', ('d', None)]
What happens next, I do not understand.
I think that yield
is turning your nice dictionary comprehension into a generator expression. So, when you're iterating over the generator, yield is "yielding" the elements that look as
, bs
..., but the statement yield ...
returns None
. So, at the end of the day, you get a dictionary looking like {'a': None, 'b': None, ...}
.
The part that confuses me is why the dictionary is actually yielded at the end. I'm guessing that this behavior is actually not well defined by the standard, but I could be wrong about that.
Interestingly enough, if you try this with a list comprehension, python complains:
>>> a = [(yield i) for i in myset]
File "<stdin>", line 1
SyntaxError: 'yield' outside function
But it's Ok in a generator (apparently).
I find! ^_^
In normal life, expression
print {item: (yield ''.join([item, 's'])) for item in myset}
evaluate like this:
def d(myset):
result = {}
for item in myset:
result[item] = (''.join([item, 's']))
yield result
print d(myset).next()
Why yield result
instead return result
? I think it is necessary to support nested list comprehensions* like this:
print {i: f.lower() for i in nums for f in fruit} # yes, it's works
So, would look like this code?
def d(myset):
result = {}
for item in myset:
result[item] = (yield ''.join([item, 's']))
yield result
and
>>> print list(d(myset))
['as', 'cs', 'bs', 'ds', {'a': None, 'b': None, 'c': None, 'd': None}]
First will be returned all values of ''.join([item, 's'])
and the last will be returned dict result
. Value of yield
expression is None
, so values in the result
is None
too.
* More correct interpretation of evaluate nested list comprehensions:
print {i: f.lower() for i in nums for f in fruit}
# eval like this:
result = {}
for i, f in product(nums, fruit): # product from itertools
key, value = (i, f.lower())
result[key] = value
print result
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:
list
calls next(mydict)
.''.join([item, 's'])
to list
and freezes the comprehension.list
calls next(mydict)
.yield
(None
) to item
in the dictionary and starts a new comprehension iteration.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.