3

When using the following code only for demonstrative purposes:

from uuid import uuid4


class router(object):

    def route(self):

        res = response(jid=str(uuid4()))

        worker = resource()
        worker.dispatch(res)

        print '[jid: %s, status: %s]' % (res.jid, res.status)


class response(object):

    def __init__(self, jid):
        self.jid = jid
        self.status = 0


class resource(object):

    def __init__(self):
        self.status = 200

    def dispatch(self, res):
        res.status = self.status
        rs = 'ok'
        #return rs
        yield rs


app = router()
app.route()

If using return rs instead of yield rs I can update the value of status within the dispatch(self, res) method of the resource class, out put is something like:

[jid: 575fb301-1aa9-40e7-a077-87887c8be284, status: 200]

But if using yield rs I can't update the value of status, I am getting always the original value, example:

[jid: 575fb301-1aa9-40e7-a077-87887c8be284, status: 0]

Therefore I would like to know, how to update the object variables of an object passed as a reference, when using yield.

nbari
  • 25,603
  • 10
  • 76
  • 131

2 Answers2

4

You need to iterate the generator. Otherwise the generator is not executed.

>>> def gen():
...     print(1)
...     yield 'blah'
...     print(2)
...
>>> g = gen() # No print (not executed)
>>> next(g)   # print 1, yield `blah`. execution suspended.
1
'blah'

Replace following line:

worker.dispatch(res)

with:

for rs in worker.dispatch(res):
    pass

or

next(worker.dispatch(res))
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • it is ok to always iterate in case sometimes I have a **yield** and others a **return** ? – nbari Feb 11 '14 at 14:24
  • @nbari, I don't understand what you mean `others a return`. – falsetru Feb 11 '14 at 14:28
  • I mean that some times I will be using **yield** and others **return**, therefore, I was thinking to always use something like ``for _ in worker.dispatch(res): pass`` so that I can get the correct result, what do you think? – nbari Feb 11 '14 at 14:33
  • @nbari, Ok I see. When you use `return`, you can't the return value (unless the return value itself is an iterable object.) – falsetru Feb 11 '14 at 14:34
  • @nbari, The point is that if you use `yield` in the function, the function become generator function. Call the generator function does not return value, but a generator. The generator is not executed until you iterate it or continue it using `next` function/method or `send` method. – falsetru Feb 11 '14 at 14:37
  • I was testing **gevent** and came across this problem, but since I am using this for a wsgi app, If I am right, the application object must return an iterable yielding zero or more strings, so I think is safe to use your suggestion ``for _ in worker.dispatch(res): pass`` – nbari Feb 11 '14 at 14:42
  • @nbari, Why do you bother to iterate it? If you use `return`, just use the return value. Choose `return` or `yield`. Then just use return value of iterate/next it accordingly. – falsetru Feb 11 '14 at 14:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/47248/discussion-between-nbari-and-falsetru) – nbari Feb 11 '14 at 15:42
2

By using yield you are telling python that your dispatch() method is a generator.

So when you call worker.dispatch(res), nothing actually happens (try to call print worker.dispatch(res), you'll see just the object reference).

You have to iterate over it as mentioned by falsetru.

See also the Python yield keyword explained

Community
  • 1
  • 1
E.Z.
  • 6,393
  • 11
  • 42
  • 69