yield:

The Python keyword you never bothered to learn

I'm Steve

Web developer at Insync

Part-time link poster at SVI

Hates semicolons

 

Iterator

>>> class countdown:
...     def __init__(self, count):
...         self.count = count
...     def __iter__(self):
...         return self
...     def next(self):
...         if self.count > 0:
...             count = self.count
...             self.count -= 1
...             return count
...         else:
...             raise StopIteration()
>>> for i in countdown(10):
...     print i
...
10
9
8
7
6
5
4
3
2
1

Generator

>>> def countdown(n):
...     while n > 0:
...         yield n
...         n -= 1
>>> for i in countdown(5):
...   print i
...
5
4
3
2
1

Generator Expressions

>>> (x**2 for x in range(5))
<generator object <genexpr> at 0x105f40be0>
>>>
>>> for i in _:
...     print i
...
0
1
4
9
16
>>> sum(x**2 for x in range(5))
30

Examples

def cat(*filenames):
    for filename in filenames:
        for line in open(filename):
            yield line

cat("file.a", "file.b", "file.c")
def quote(text, prefix=">"):
    return "\n".join(
        prefix + " " + line
        for line in text.split("\n")
    )
        

>>> print quote("""Our acuse cleaned the tofu's
... sense sanely, really hazardous.
...
... A sick trunk mixes one's milky dilemma of simple
... mothers before cheeses without my local library.
""")
> Our acuse cleaned the tofu's
> sense sanely, really hazardous.
>
> A sick trunk mixes one's milky dilemma of simple
> mothers before cheeses without my local library.
def process_order(user, order):
    if not user.has_enough_funds():
        yield "Not enough funds"

    if not check_order(order):
        yield "Invalid order"

    order.charge(user)
@app.route('/buy')
def buy():
    user = get_user()
    order = get_order()
    errors = []
    if request.method == 'POST':
        errors = list(process_order(user, order))
        if not errors:
            return redirect('/thanks')
    return render_template('buy.html', errors=errors)
            
    
from contextlib import contextmanager

@contextmanager
def transaction():
    try:
        session = create_session()
        yield session
    except:
        session.rollback()
    else:
        session.commit()

with transaction() as session:
    session.add(User(name="Steve"))
    session.add(User(name="Mark"))

Coroutines

PEP-342: yield as an expression

def fizzbuzz():
    try:
        while True:
            number = (yield)
            foo = ""
            if number % 3 == 0:
                foo += "Fizz"
            if number % 5 == 0:
                foo += "Buzz"
            print foo if foo else number
    except GeneratorExit:
        print "Bye!"
>>> cor = fizzbuzz()
>>> cor.next() # Prime it!
>>> for x in range(1, 16):
...     cor.send(x)
...
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
>>> cor.close()
Bye!
def coroutine(func):
    def start(*args, **kwargs):
        cor = func(*args, **kwargs)
        cor.next()
        return cor
    return start

@coroutine
def fizzbuzz():
    ...
>>> fb = fizzbuzz()
>>> for x in range(1, 6):
...     fb.send(x)
...
1
2
Fizz
4
Buzz
>>> fb.close()
Bye!
>>>

Dave Beazly's talks on Generators

questions = (
    person.ask() for person in room
    if person.has_question()
)

answers = (
    question.answer() for question in questions
)

if all(answers):
    yield next_talk

yield:

By Mark Steve Samson