yield

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • km

    yield

    Hi all,

    i didnt understand the purpose of 'yield' keyword and the concept of 'generators' in python. can someone explain me with a small example how generators differ from normal function calls?
    kindly enlighten
    regards,
    KM




  • Mark Borgerding

    #2
    Re: yield

    km wrote:[color=blue]
    > Hi all,
    >
    > i didnt understand the purpose of 'yield' keyword and the concept of 'generators' in python. can someone explain me with a small example how generators differ from normal function calls?
    > kindly enlighten
    > regards,
    > KM
    >[/color]

    I think of it as an "interrupt" that provides a value back to the caller
    for each value in a sequence. It can also be loosely thought of as an
    iterator(sort of).



    Here's an example that may (or may not) help:


    def foo():
    print 'entering foo function'
    for i in range(3):
    print 'yielding %s' % i
    yield i
    print 'leaving foo function'

    for x in foo():
    print 'x is %s' % x

    ############### #
    prints out the following:

    entering foo function
    yielding 0
    x is 0
    yielding 1
    x is 1
    yielding 2
    x is 2
    leaving foo function

    Comment

    • Isaac To

      #3
      Re: yield

      >>>>> "km" == km <km@mrna.tn.nic .in> writes:

      km> Hi all, i didnt understand the purpose of 'yield' keyword and the
      km> concept of 'generators' in python. can someone explain me with a
      km> small example how generators differ from normal function calls?

      Normally, when you define a function and call it, the code within the
      function gets executed, until the function returns, and at that point the
      function disappear altogether. For example:
      [color=blue][color=green][color=darkred]
      >>> def f():[/color][/color][/color]
      .... a = 1
      .... while a < 32:
      .... print 'Hello, ' + str(a)
      .... return a
      .... a *= 2
      ....[color=blue][color=green][color=darkred]
      >>> f()[/color][/color][/color]
      Hello, 1
      1

      Here we define f, and at the time of definition the statements of the
      function won't be executed. Instead it is executed when the function is
      called. Once the function returns, the function disappear completely, with
      all the context in the function: all the statements after the return
      statement (a*=2 here) won't be executed, and the "other iterations" of the
      loop will no longer execute. Here, after printing 'Hello, 1', the function
      stops, returning a value 1 to the caller (which is printed by the Python
      interpretor).

      When a function contains the "yield" keyword, the story is somewhat
      different. If we simply replace the "return" keyword with the "yield"
      keyword in the above example we get this:
      [color=blue][color=green][color=darkred]
      >>> def f():[/color][/color][/color]
      .... a = 1
      .... while a < 32:
      .... print 'Hello, ' + str(a)
      .... yield a
      .... a *= 2
      ....[color=blue][color=green][color=darkred]
      >>> f()[/color][/color][/color]
      <generator object at 0x4022136c>

      In other words, the statements within the function will not be invoked even
      when we call f()! Instead, calling f() returns you an object, which is said
      to be a "generator" object. The basic method of this object is "next". For
      example:
      [color=blue][color=green][color=darkred]
      >>> g = f()
      >>> g.next()[/color][/color][/color]
      Hello, 1
      1

      So, the effect of f().next() in the function using "yield" is exactly the
      same as f() in the function using "return". The difference in the current
      version is that we still have an object g, so... we can call next() again!
      [color=blue][color=green][color=darkred]
      >>> g.next()[/color][/color][/color]
      Hello, 2
      2[color=blue][color=green][color=darkred]
      >>> g.next()[/color][/color][/color]
      Hello, 4
      4[color=blue][color=green][color=darkred]
      >>> g.next()[/color][/color][/color]
      Hello, 8
      8[color=blue][color=green][color=darkred]
      >>> g.next()[/color][/color][/color]
      Hello, 16
      16[color=blue][color=green][color=darkred]
      >>> g.next()[/color][/color][/color]
      Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      StopIteration

      In other words, after the function f yields, the function and the execution
      context has not disappear. Instead it is still stored within the generator
      object, waiting for you to call next() again. At that point the function
      continues its execution. If during the execution the function yields
      another value, g.next() returns the value of that value and f is stopped
      again. If during the execution the function returns, g.next() throws an
      exception StopIteration (and will do that again if you call g.next() again).

      This is a powerful construct: this is the only Python construct that let you
      easily "remember" the dynamic execution structure. (Most other language
      lacks that facility so it is impossible to remember the dynamic execution
      structure, short of packaging all the information you want in structure.)
      In the above example, after the first execution of next(), the g object
      remembers (in its "frame object", which can be located by g.gi_frame) what
      local variables are defined and what values they hold, which line the
      function is currently executing at, and what global variables are currently
      visible:
      [color=blue][color=green][color=darkred]
      >>> g = f()
      >>> g.next()[/color][/color][/color]
      Hello, 1
      1[color=blue][color=green][color=darkred]
      >>> dir(g.gi_frame)[/color][/color][/color]
      ['__class__', '__delattr__', '__doc__', '__getattribute __', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__' , '__repr__', '__setattr__', '__str__', 'f_back', 'f_builtins', 'f_code', 'f_exc_tracebac k', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace'][color=blue][color=green][color=darkred]
      >>> g.gi_frame.f_lo cals[/color][/color][/color]
      {'a': 1}[color=blue][color=green][color=darkred]
      >>> g.gi_frame.f_li neno[/color][/color][/color]
      5[color=blue][color=green][color=darkred]
      >>> g.gi_frame.f_gl obals[/color][/color][/color]
      {'__builtins__' : <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, 'g': <generator object at 0x4022146c>, 'f': <function f at 0x40219f44>}

      The simplest way to use the construct, and also by far the most common use
      of it, is to use it as an "iterator", i.e., to repeatedly call it in a loop
      until it finishes throw a StopIteration exception. E.g.,
      [color=blue][color=green][color=darkred]
      >>> for i in f():[/color][/color][/color]
      .... print i * 3 + 7
      ....
      Hello, 1
      10
      Hello, 2
      13
      Hello, 4
      19
      Hello, 8
      31
      Hello, 16
      55

      Here the "for" loop will call the next() method of the generator returned by
      f() repeatedly, each time getting the returned value into the variable i and
      execute the "body" of the for loop (which prints the value i*3+7). Similar
      things happen in list-comprehensions:
      [color=blue][color=green][color=darkred]
      >>> [i * 3 + 7 for i in f()][/color][/color][/color]
      Hello, 1
      Hello, 2
      Hello, 4
      Hello, 8
      Hello, 16
      [10, 13, 19, 31, 55]

      Note that, apart from printing the 'Hello, n' messages, both of the above
      have the end-effect that f() is the list [1, 2, 4, 8, 16]. So you can treat
      a generator as a "lazy" list that (1) an element will be generated
      "on-the-fly" when next() is called, and thus may be affected by the changes
      in the environment like the global variables, and may in reverse affect the
      environment like printing message and changing the global variables; and (2)
      after producing the element, the element itself will not be kept by the
      generator.

      Regards,
      Isaac.

      Comment

      • John J. Lee

        #4
        Re: yield

        Isaac To <kkto@csis.hku. hk> writes:
        [color=blue][color=green][color=darkred]
        > >>>>> "km" == km <km@mrna.tn.nic .in> writes:[/color][/color]
        >
        > km> Hi all, i didnt understand the purpose of 'yield' keyword and the
        > km> concept of 'generators' in python. can someone explain me with a
        > km> small example how generators differ from normal function calls?
        >
        > Normally, when you define a function and call it, the code within the
        > function gets executed, until the function returns, and at that point the
        > function disappear altogether. For example:[/color]
        [...]

        That's a misleading way of putting it: the *function* doesn't
        disappear (you can still call it after it's finished), but its state
        of execution does (you can't access its local variables after it's
        finished, except by calling it again and getting a *new* set of local
        variables).


        John

        Comment

        Working...