wiki:python/Types/generator

send, yield

See also:

iter


Python Doc: Iterator Types
Python Doc: Generator Types
Python Doc: Yield Expression
Python PEP: 342


generator `__str__()` Methode überschreiben?
Generator vs List-Comprehension vs while-Schleife...
Generator stoppen...


generator.send()

Funktionsweise von Generatoren unklar...

def gen(value):
    while True:
        # `value` will become the assignment via `send()`,
        # not the tuple `(value + 1, value + 2)`! The
        # tuple will be yielded not assigned!
        value = yield (value + 1, value + 2)
>>> dis.dis(gen)
  2           0 SETUP_LOOP              17 (to 20)
        >>    3 LOAD_GLOBAL              0 (True)
              6 POP_JUMP_IF_FALSE       19

  3           9 LOAD_FAST                0 (value)
             12 YIELD_VALUE         
             13 STORE_FAST               0 (value)
             16 JUMP_ABSOLUTE            3
        >>   19 POP_BLOCK           
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

The python doc says:

TOS
Top Of Stack

SETUP_LOOP(delta)
Pushes a block for a loop onto the block stack. The block spans from the current instruction with a size of delta bytes.

LOAD_GLOBAL(namei)
Loads the global named co_names[namei] onto the stack.

POP_JUMP_IF_FALSE(target)
If TOS is false, sets the bytecode counter to target. TOS is popped.

LOAD_FAST(var_num)
Pushes a reference to the local co_varnames[var_num] onto the stack.

YIELD_VALUE()
Pops TOS and yields it from a generator.

STORE_FAST(var_num)
Stores TOS into the local co_varnames[var_num].

JUMP_ABSOLUTE(target)
Set bytecode counter to target.

POP_BLOCK()
Removes one block from the block stack. Per frame, there is a stack of blocks, denoting nested loops, try statements, and such.

LOAD_CONST(consti)
Pushes co_consts[consti] onto the stack.

RETURN_VALUE()
Returns with TOS to the caller of the function.

Examples

Generator 'ping-pong'

Möglichkeiten zur Suche bzw. Abfrage von Objekten

The following example will demonstrate how a search in the denkdran.utils.search module will work. The expression in this example will process a simple

>>> (1 > 2) and (3 > 4)
False

Within this example obj is declared but not used. Cause obj is needed by denkdran.utils.search.OperateByCall I didn't want strike off it within the explanation.

class Constant(object):
    def __init__(self, value):
        self.value = value

    def submit(self, obj=None):
        yield self.value, None


class And(object):
    def __init__(self, left, right):
        self.left, self.right = left, right

    def submit(self, obj):
        left_value = yield None, self.left.submit(obj)
        right_value = yield None, self.right.submit(obj)
        yield left_value and right_value, None


class GT(object):
    def __init__(self, left, right):
        self.left, self.right = left, right

    def submit(self, obj):
        left_value = yield None, self.left.submit(obj)
        right_value = yield None, self.right.submit(obj)
        yield left_value > right_value, None


def submit(expression, obj):
    stack = [expression.submit(obj)]
    value = None
    while stack:
        try:
            value, expression = stack[-1].send(value)
        except StopIteration:
            stack.pop()
        if expression is not None:
            stack.append(expression)
    return value
expression = And(
    GT(Constant(1), Constant(2)),
    GT(Constant(3), Constant(4))
)

While reading the following diagram keep in mind what will be happened while processing e.g. the GT.generator:

  • submit() only creates the generator
  • The first generator.send() (which must be have None as argument) starts the generator and processes the first yield statement after the assignment operator. Imagine that there exists a 'code cursor' which now pauses on the assignment operator. On this time no assignment to left_value happens.
  • The second generator.send() firstly assigns the passed argument to the name (left_value) before the assignment operator and than the second yield will be processed. The 'code cursor' now pauses on the second assignment operator. Once again: No assignment to right_value yet!
  • The third generator.send() firstly assigns the passed argument to the name (right_value) before the assignment operator and than the last yield will be processed.
  • Any further generator.send() or generator.next() will be raise a StopIteration exception.
submit(expression, obj=None)
       ↓
       And.submit(obj)
       ↓                                                                            ,--- still not defined   ,--- GT(Constant(1), Constant(2))
       [And.generator]                                                              ↓                        ↓
       ↓                                                                            left_value = yield None, self.left.submit(obj)
       And.generator.send(value=None) ______________________first `send`_________________________↑
       ↓
       (None, GT(1, 2).generator)
              ↓                                                                     ,--- still not defined   ,--- Constant(1)
              [And.generator, GT(1, 2).generator]                                   ↓                        ↓
              ↓                                                                     left_value = yield None, self.left.submit(obj)
              GT(1, 2).generator.send(value=None) __________first `send`_________________________↑
              ↓
              (None, Constant(1).generator)
                     ↓                                                                    ,--- 1
                     [And.generator, GT(1, 2).generator, Constant(1).generator]           ↓
                     ↓                                                              yield self.value, None
                     Constant(1).generator.send(value=None) ________________________↑
                     ↓
                     (1, None)                                                   ,-------------.
              ,------'                                                           |  ,--- 1     |
              ↓                                                                  |  ↓          ↓
              [And.generator, GT(1, 2).generator]                                |  left_value = [already processed]
              |                                                                  |  ,--- still not defined    ,--- Constant(2)
              |                                                                  |  ↓                         ↓
              ↓                                                                  |  right_value = yield None, self.right.submit(obj)
              GT(1, 2).generator.send(value=1) _____________second `send`________;________________↑
              ↓
              (None, Constant(2).generator)
                     ↓                                                                    ,--- 2
                     [And.generator, GT(1, 2).generator, Constant(2).generator]           ↓
                     ↓                                                              yield self.value, None
                     Constant(2).generator.send(value=None) ________________________↑
                     ↓
                     (2, None)                                                   ,--------------.
              ,------'                                                           |  ,--- 2      |
              ↓                                                                  |  ↓           ↓
              [And.generator, GT(1, 2).generator]                                |  right_value = [already processed]
              |                                                                  |        ,--- 1       ,--- 2
              |                                                                  |        ↓            ↓
              ↓                                                                  |  yield left_value > right_value, None
              GT(1, 2).generator.send(value=2) _____________third `send`_________;__↑
              ↓
              (False, None)
       ,------'
       ↓
       [And.generator]
       ↓
       And.generator.send(value=False) _____________________second `send`_________...
       ↓
       (None, GT(3, 4).generator)
              ↓
              [And.generator, GT(3, 4).generator]
              ↓
              GT(3, 4).generator.send(value=None)
              ↓
              (None, Constant(3).generator)
                     ↓
                     [And.generator, GT(3, 4).generator, Constant(3).generator]
                     ↓
                     Constant(3).generator.send(value=None)
                     ↓
                     (3, None)
              ,------'
              ↓
              [And.generator, GT(3, 4).generator]
              ↓
              GT(3, 4).generator.send(value=3)
              ↓
              (None, Constant(4).generator)
                     ↓
                     [And.generator, GT(3, 4).generator, Constant(4).generator]
                     ↓
                     Constant(4).generator.send(value=None)
                     ↓
                     (4, None)
              ,------'
              ↓
              [And.generator, GT(3, 4).generator]
              ↓
              GT(3, 4).generator.send(value=4)
              ↓
              (False, None)
       ,------'
       ↓
       [And.generator]
       ↓
       ↓
       And.generator.send(value=False)
       ↓
       (False, None)
,------'
↓
[]
↓
False
Last modified 4 years ago Last modified on Dec 2, 2015, 2:23:32 PM