Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can yield be indexed?

I thought I could make my python (2.7.10) code simpler by directly accessing the index of a value passed to a generator via send, and was surprised the code ran. I then discovered an index applied to yield doesn't really do anything, nor does it throw an exception:

def gen1():
    t = yield[0]
    assert t
    yield False

g = gen1()
next(g)
g.send('char_str')

However, if I try to index yield thrice or more, I get an exception:

def gen1():
    t = yield[0][0][0]
    assert t
    yield False

g = gen1()
next(g)
g.send('char_str')

which throws

TypeError: 'int' object has no attribute '__getitem__'

This was unusually inconsistent behavior, and I was wondering if there is an intuitive explanation for what indexing yield is actually doing?

like image 441
ecoe Avatar asked Oct 15 '16 16:10

ecoe


1 Answers

You are not indexing. You are yielding a list; the expression yield[0] is really just the same as the following (but without a variable):

lst = [0]
yield lst

If you look at what next() returned you'd have gotten that list:

>>> def gen1():
...   t = yield[0]
...   assert t
...   yield False
...
>>> g = gen1()
>>> next(g)
[0]

You don't have to have a space between yield and the [0], that's all.

The exception is caused by you trying to apply the subscription to the contained 0 integer:

>>> [0]        # list with one element, the int value 0
[0]
>>> [0][0]     # indexing the first element, so 0
0
>>> [0][0][0]  # trying to index the 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not subscriptable

If you want to index a value sent to the generator, put parentheses around the yield expression:

t = (yield)[0]

Demo:

>>> def gen1():
...     t = (yield)[0]
...     print 'Received: {!r}'.format(t)
...     yield False
...
>>> g = gen1()
>>> next(g)
>>> g.send('foo')
Received: 'f'
False
like image 193
Martijn Pieters Avatar answered Oct 15 '22 10:10

Martijn Pieters