Using a generator function is a bit different than using a regular function. For the basics, see Chapter 20 of the Learning Python tutorial by Mark Lutz. For enlightenment to David Beazley's presentations - " Generator Tricks for Systems Programmers ", " A Curious Course on Coroutines and Concurrency " and " Generators: The Final Frontier ". First example:
def acc(): buf = [] while True: # Получаем отправленные в генератор значения value, command = yield buf.append(value) if command: break yield buf # Получаем генератор g = acc() # Инициализируем генератор next(g) # Отправляем в генератор значения g.send((1, False)) g.send((2, False)) g.send((3, False)) # Получаем накопленные значения из генератора items = g.send((4, True))
We will get rid of the initialization step using the decorator, and at the same time from having to send tuples to the generator:
def coroutine(func): def init(*args, **kwargs): cr = func(*args, **kwargs) next(cr) return cr return init @coroutine def acc(): buf = [] while True: value = yield if value is StopIteration: break buf.append(value) yield buf g = acc() g.send(1) g.send(2) g.send(3) items = g.send(StopIteration)
Now let's try to eliminate the need to call send
:
def coroutine(func): def init(*args, **kwargs): cr = func(*args, **kwargs) next(cr) return lambda x: cr.send(x) return init @coroutine def acc(): ... g = acc() g(1) g(2) g(3) items = g(StopIteration)
The same effect can be achieved with the help of a closure :
def acc(): buf = [] def f(item): if item is None: return buf buf.append(item) return f a = acc() a(1) a(2) a(3) items = a(None)
Or using the function attributes (for educational purposes only!):
def acc(item): if not hasattr(acc, '_buf'): acc._buf = [] if item is None: items, acc._buf = acc._buf, [] return items acc._buf.append(item) acc(1) acc(2) acc(3) items = acc(None)