Generators and their next() and send() methods

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

    Generators and their next() and send() methods

    Hello,

    I was playing around a bit with generators using next() and send(). And
    I was wondering why an extra send() method was introduced instead of
    simply allowing an argument for next().

    Also, I find it a bit counter-intuitive that send(42) not only "sets"
    the generator to the specified value, but yields the next value at the
    same time.

    As an exercise, I wanted to somehow "modify" a generator so that it
    would have a next() method accepting an argument, something like

    @myway
    def gen():
    pass

    But I failed to come up with an implementation of the myway() function.

    Any comments and/or suggestions?

    Greetings,
    Thomas

    --
    Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
    (Coluche)
  • Arnaud Delobelle

    #2
    Re: Generators and their next() and send() methods

    Thomas Mlynarczyk <thomas@mlynarc zyk-webdesign.dewri tes:
    Hello,
    >
    I was playing around a bit with generators using next() and
    send(). And I was wondering why an extra send() method was introduced
    instead of simply allowing an argument for next().
    >
    Also, I find it a bit counter-intuitive that send(42) not only "sets"
    the generator to the specified value, but yields the next value at the
    same time.
    If you want to simply 'set' the generator (by which I take you mean
    'change its state') without without iterating it one step, then what you
    need is a class with an __iter__() method. Then you can change the
    state of the object between calls to next(). E.g.
    >>class MyGenerator(obj ect):
    .... def __init__(self, v): self.v = v
    .... def __iter__(self):
    .... for x in range(10):
    .... yield self.v
    ....
    >>g = MyGenerator(5)
    >>for i in g:
    .... g.v = input("val:")
    .... print i
    ....
    val:4
    5
    val:10
    4
    val:34
    10
    val:56
    34
    val:

    Etc...

    --
    Arnaud

    Comment

    • Thomas Mlynarczyk

      #3
      Re: Generators and their next() and send() methods

      Arnaud Delobelle schrieb:
      If you want to simply 'set' the generator (by which I take you mean
      'change its state') without without iterating it one step, then what you
      need is a class with an __iter__() method. Then you can change the
      state of the object between calls to next(). E.g.
      >>>class MyGenerator(obj ect):
      ... def __init__(self, v): self.v = v
      ... def __iter__(self):
      ... for x in range(10):
      ... yield self.v
      ...
      >>>g = MyGenerator(5)
      >>>for i in g:
      ... g.v = input("val:")
      ... print i
      That's a good idea, thanks! Meanwhile I have succeeded in writing a
      decorator which allows an argument for next():

      def myway( g ):
      class mygen( object ):
      def __init__( self, n ):
      self.g = g( n )
      def __iter__( self ):
      return self
      def next( self, s = None ):
      return self.g.next() if s is None else self.g.send( s )
      def send( self, s ):
      return self.g.send( s )
      return mygen

      @myway
      def gen( n ):
      i = 0
      while i <= n:
      s = yield i
      if s is not None:
      i = s
      else:
      i += 1

      Of course, this is just an exercise -- it doesn't look as if it's worth
      using @mayway in any "real" code just to be able to write g.next(42)
      instead of g.send(42). Still, I would like to know why it was decided to
      introduce a send() method instead of allowing an argument for next().

      Thanks and greetings,
      Thomas


      --
      Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
      (Coluche)

      Comment

      • alex23

        #4
        Re: Generators and their next() and send() methods

        On Nov 17, 7:36 am, Thomas Mlynarczyk <tho...@mlynarc zyk-webdesign.de>
        wrote:
        Still, I would like to know why it was decided to
        introduce a send() method instead of allowing an argument for next().
        Hey Thomas,

        A great place to gain insight into the reasoning behind changes to
        Python is the PEPs:

        This PEP proposes some enhancements to the API and syntax of generators, to make them usable as simple coroutines. It is basically a combination of ideas from these two PEPs, which may be considered redundant if this PEP is accepted:


        That links to the original proposal to extend the generator behaviour
        to include send. I haven't the time right now to read it but if
        anything is going to explain the choice, it'll be there.

        At a guess, I'd expect a new method was chosen to provide semantic
        distinctness with the original behaviour. Or maybe so it wouldn't
        break any existing code with decorators such as your's :)

        Comment

        • Aaron Brady

          #5
          Re: Generators and their next() and send() methods

          On Nov 16, 3:36 pm, Thomas Mlynarczyk <tho...@mlynarc zyk-webdesign.de>
          wrote:
          Arnaud Delobelle schrieb:
          >
          If you want to simply 'set' the generator (by which I take you mean
          'change its state') without without iterating it one step, then what you
          need is a class with an __iter__() method.  Then you can change the
          state of the object between calls to next().  E.g.
          >>class MyGenerator(obj ect):
          ...     def __init__(self, v): self.v = v
          ...     def __iter__(self):
          ...         for x in range(10):
          ...             yield self.v
          ...
          >>g = MyGenerator(5)
          >>for i in g:
          ...     g.v = input("val:")
          ...     print i
          >
          That's a good idea, thanks! Meanwhile I have succeeded in writing a
          decorator which allows an argument for next():
          Here's a different structure if you want it. Untested.

          def myway( g ):
          def init( *ar, **kw ):
          g= g( *ar, **kw )
               class mygen( object ):
                   def __iter__( self ):
                       return self
                   def next( self, s= None ):
                       return g.next() if s is None else g.send( s )
                   def send( self, s ):
                       return g.send( s )
               return mygen
          return init

          And, if you don't intend to use 'myway' on 'listiterator's and such,
          'send( None )' is equivalent to 'next( )'.

          def myway( g ):
          def init( *ar, **kw ):
          g= g( *ar, **kw )
          class mygen( object ):
          def __iter__( self ):
          return self
          def next( self, s= None ):
          return g.send( s )
          def send( self, s ):
          return g.send( s )
          return mygen
          return init

          There's probably a typo in it.

          Comment

          • Thomas Mlynarczyk

            #6
            Re: Generators and their next() and send() methods

            alex23 schrieb:
            This PEP proposes some enhancements to the API and syntax of generators, to make them usable as simple coroutines. It is basically a combination of ideas from these two PEPs, which may be considered redundant if this PEP is accepted:

            That links to the original proposal to extend the generator behaviour
            After some searching, I found this as a remark in parentheses:

            "Introducin g a new method instead of overloading next() minimizes
            overhead for simple next() calls."

            And also a (little) more detailed explanation. So, basically, allowing
            an argument for next() would have meant more overhead in the Python
            implementation. Still, I don't quite see why, as this could be detected
            at compile time, couldn't it?

            On the other hand, if I understood correctly, I can use send(None) as an
            equivalent of next(). So, while I cannot have next(argument) instead of
            send(argument), I can have send(None) instead of next().
            At a guess, I'd expect a new method was chosen to provide semantic
            distinctness with the original behaviour.
            But the semantics would not have changed, they would only be "extended"
            and thus remain "compatible ", wouldn't they?

            Greetings,
            Thomas

            --
            Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
            (Coluche)

            Comment

            • Thomas Mlynarczyk

              #7
              Re: Generators and their next() and send() methods

              Aaron Brady schrieb:
              And, if you don't intend to use 'myway' on 'listiterator's and such,
              'send( None )' is equivalent to 'next( )'.
              I didn't know that. But doesn't that impose a restriction somehow? It
              makes it impossible to send a None to a generator.

              Greetings,
              Thomas

              --
              Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
              (Coluche)

              Comment

              • Aaron Brady

                #8
                Re: Generators and their next() and send() methods

                On Nov 18, 5:20 pm, Thomas Mlynarczyk <tho...@mlynarc zyk-webdesign.de>
                wrote:
                Aaron Brady schrieb:
                >
                And, if you don't intend to use 'myway' on 'listiterator's and such,
                'send( None )' is equivalent to 'next( )'.
                >
                I didn't know that. But doesn't that impose a restriction somehow? It
                makes it impossible to send a None to a generator.
                No, None is a valid value to use with send. Not all iterators support
                send.

                Comment

                Working...