Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Иван Гончарук. Корутины для самых маленьких

Иван Гончарук. Корутины для самых маленьких

Доклад будет посвящен механизму работы корутин в ЯП Python: какое отношение к ним имеют генераторы, как генераторы превратить в корутины и как реализовать простейший event-loop для асинхронного выполнения корутин.

Python Community Chelyabinsk

December 08, 2018
Tweet

More Decks by Python Community Chelyabinsk

Other Decks in Programming

Transcript

  1. Asyncio 2 async def coroutine(): result = 0 for index

    in range(10): result += index print('Now awaiting!') await asyncio.sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): await asyncio.sleep(0) print('Tick!')
  2. Asyncio 3 async def coroutine(): result = 0 for index

    in range(10): result += index print('Now awaiting!') await asyncio.sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): await asyncio.sleep(0) print('Tick!') >>> loop = asyncio.get_event_loop() >>> loop.create_task(tick()) >>> loop.create_task(tock()) >>> loop.run_forever()
  3. Asyncio 4 async def coroutine(): result = 0 for index

    in range(10): result += index print('Now awaiting!') await asyncio.sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): await asyncio.sleep(0) print('Tick!') >>> loop = asyncio.get_event_loop() >>> loop.create_task(tick()) >>> loop.create_task(tock()) >>> loop.run_forever() Now awaiting! Tick!
  4. Asyncio 5 async def coroutine(): result = 0 for index

    in range(10): result += index print('Now awaiting!') await asyncio.sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): await asyncio.sleep(0) print('Tick!') >>> loop = asyncio.get_event_loop() >>> loop.create_task(tick()) >>> loop.create_task(tock()) >>> loop.run_forever() Now awaiting! Tick! ...
  5. Asyncio 6 async def coroutine(): result = 0 for index

    in range(10): result += index print('Now awaiting!') await asyncio.sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): await asyncio.sleep(0) print('Tick!') >>> loop = asyncio.get_event_loop() >>> loop.create_task(tick()) >>> loop.create_task(tock()) >>> loop.run_forever() Now awaiting! Tick! ... Now awaiting! Tick!
  6. Asyncio 7 async def coroutine(): result = 0 for index

    in range(10): result += index print('Now awaiting!') await asyncio.sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): await asyncio.sleep(0) print('Tick!') >>> loop = asyncio.get_event_loop() >>> loop.create_task(tick()) >>> loop.create_task(tock()) >>> loop.run_forever() Now awaiting! Tick! ... Now awaiting! Tick! 45
  7. Дисклеймер • Мало теории, много примеров, 99% кода; • Категорически

    не рекомендуется использовать увиденное в реальных боевых проектах. 10
  8. Корутины (сопрограммы) • Обычные подпрограммы, но: ◦ корутина может быть

    приостановлена в произвольной точке; ◦ сохраняет свое состояние; 15
  9. Корутины (сопрограммы) • Обычные подпрограммы, но: ◦ корутина может быть

    приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; 16
  10. Корутины (сопрограммы) • Обычные подпрограммы, но: ◦ корутина может быть

    приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. 17
  11. Корутины (сопрограммы) • Обычные подпрограммы, но: ◦ корутина может быть

    приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. • Обобщенные подпрограммы; 18
  12. Корутины (сопрограммы) • Обычные подпрограммы, но: ◦ корутина может быть

    приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. • Обобщенные подпрограммы; • Позволяют реализовать кооперативную многозадачность. 19
  13. Корутины (сопрограммы) • Не выполняются параллельно*; • Нет "магического" прироста

    производительности; • В CPU-bound задачах не быстрее последовательного выполнения; 25
  14. Корутины (сопрограммы) • Не выполняются параллельно*; • Нет "магического" прироста

    производительности; • В CPU-bound задачах не быстрее последовательного выполнения; ◦ ThreadPool или ProcessPool; 26
  15. Корутины (сопрограммы) • Не выполняются параллельно*; • Нет "магического" прироста

    производительности; • В CPU-bound задачах не быстрее последовательного выполнения; ◦ ThreadPool или ProcessPool; • Большой прирост производительности в IO-bound задачах. 27
  16. Корутины (сопрограммы) • Не выполняются параллельно*; • Нет "магического" прироста

    производительности; • В CPU-bound задачах не быстрее последовательного выполнения; ◦ ThreadPool или ProcessPool; • Большой прирост производительности в IO-bound задачах. 28 Возможно реализовать схему "Одна корутина — один поток".
  17. Корутины (сопрограммы) • Обычные подпрограммы, но: ◦ корутина может быть

    приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. • Обобщенные подпрограммы; • Позволяют реализовать кооперативную многозадачность. 29
  18. Генераторы def generator(): index = 0 while True: yield index

    index += 1 31 >>> gen = generator() PEP 255: Simple Generators
  19. Генераторы def generator(): index = 0 while True: yield index

    index += 1 32 >>> gen = generator() >>> next(gen) 0 PEP 255: Simple Generators
  20. Генераторы def generator(): index = 0 while True: yield index

    index += 1 33 >>> gen = generator() >>> next(gen) 0 >>> next(gen) 1 PEP 255: Simple Generators
  21. Генераторы def generator(): index = 0 while True: yield index

    index += 1 34 >>> gen = generator() >>> next(gen) 0 >>> next(gen) 1 >>> next(gen) 2 PEP 255: Simple Generators
  22. Генераторы def generator(): index = 0 while True: yield index

    index += 1 35 >>> gen = generator() >>> next(gen) 0 >>> next(gen) 1 >>> next(gen) 2 Функция-генератор PEP 255: Simple Generators
  23. Генераторы def generator(): index = 0 while True: yield index

    index += 1 36 >>> gen = generator() >>> next(gen) 0 >>> next(gen) 1 >>> next(gen) 2 Функция-генератор Объект-генератор PEP 255: Simple Generators
  24. Генераторы def generator(): index = 0 while True: step =

    yield index index += step or 1 37 PEP 342: Coroutines via Enhanced Generators
  25. Генераторы def generator(): index = 0 while True: step =

    yield index index += step or 1 38 >>> gen = generator() PEP 342: Coroutines via Enhanced Generators
  26. Генераторы def generator(): index = 0 while True: step =

    yield index index += step or 1 39 >>> gen = generator() >>> next(gen) 0 PEP 342: Coroutines via Enhanced Generators
  27. Генераторы def generator(): index = 0 while True: step =

    yield index index += step or 1 40 >>> gen = generator() >>> next(gen) 0 >>> gen.send(10) 10 PEP 342: Coroutines via Enhanced Generators
  28. Генераторы def generator(): index = 0 while True: step =

    yield index index += step or 1 41 >>> gen = generator() >>> next(gen) 0 >>> gen.send(10) 10 10 PEP 342: Coroutines via Enhanced Generators
  29. Генераторы def generator(): index = 0 while True: step =

    yield index index += step or 1 42 >>> gen = generator() >>> next(gen) 0 >>> gen.send(10) 10 >>> next(gen) 11 10 PEP 342: Coroutines via Enhanced Generators
  30. Генераторы def generator(): index = 0 while True: try: yield

    index index += 1 except ValueError: print('Oh!') 43 >>> gen = generator() >>> next(gen) 0 >>> gen.throw(ValueError) Oh! 0 PEP 342: Coroutines via Enhanced Generators
  31. Генераторы def generator(): index = 0 while True: try: yield

    index index += 1 except ValueError: print('Oh!') 44 >>> gen = generator() >>> next(gen) 0 >>> gen.throw(ValueError) Oh! 0 ValueError PEP 342: Coroutines via Enhanced Generators
  32. Генераторы def generator(): index = 0 while True: try: yield

    index index += 1 except ValueError: print('Oh!') 45 >>> gen = generator() >>> next(gen) 0 >>> gen.throw(ValueError) Oh! 0 >>> next(gen) 1 ValueError PEP 342: Coroutines via Enhanced Generators
  33. Генераторы def generator(): index = 0 while True: yield index

    index += 1 46 >>> gen = generator() >>> next(gen) 0 >>> gen.close() PEP 342: Coroutines via Enhanced Generators
  34. Генераторы def generator(): index = 0 while True: yield index

    index += 1 47 >>> gen = generator() >>> next(gen) 0 >>> gen.close() GeneratorExit PEP 342: Coroutines via Enhanced Generators
  35. Генераторы def generator(): index = 0 while True: yield index

    index += 1 48 >>> gen = generator() >>> next(gen) 0 >>> gen.close() >>> next(gen) StopIteration GeneratorExit PEP 342: Coroutines via Enhanced Generators
  36. Генераторы def generator(): index = 0 while True: try: yield

    index index += 1 except GeneratorExit: print('Oh!') 49 PEP 342: Coroutines via Enhanced Generators
  37. Генераторы def generator(): index = 0 while True: try: yield

    index index += 1 except GeneratorExit: print('Oh!') 50 >>> gen = generator() >>> next(gen) 0 >>> gen.close() Oh! RuntimeError: generator ignored GeneratorExit PEP 342: Coroutines via Enhanced Generators
  38. Генераторы def generator(): index = 0 while True: try: yield

    index index += 1 except GeneratorExit: print('Oh!') break 51 >>> gen = generator() >>> next(gen) 0 >>> gen.close() Oh! >>> next(gen) StopIteration PEP 342: Coroutines via Enhanced Generators
  39. Генераторы def generator(): try: index = 0 while True: yield

    index index += 1 except GeneratorExit: print('Oh!') 52 >>> gen = generator() >>> next(gen) 0 >>> gen.close() Oh! >>> next(gen) StopIteration PEP 342: Coroutines via Enhanced Generators
  40. Генераторы def generator(): try: index = 0 while True: yield

    index index += 1 except GeneratorExit: print('Oh!') 53 >>> gen = generator() >>> next(gen) 0 >>> del gen Oh! PEP 342: Coroutines via Enhanced Generators
  41. Генераторы def recscan(path): for entry in os.scandir(path): if entry.is_dir(): yield

    entry for child in recscan(entry.path): yield child else: yield entry 54 PEP 380: Syntax for Delegating to a Subgenerator
  42. Генераторы def recscan(path): for entry in os.scandir(path): if entry.is_dir(): yield

    entry for child in recscan(entry.path): yield child else: yield entry 55 >>> for entry in recscan('/some/path'): ... print(entry.name) ... PEP 380: Syntax for Delegating to a Subgenerator
  43. Генераторы def recscan(path): for entry in os.scandir(path): if entry.is_dir(): yield

    entry for child in recscan(entry.path): yield child else: yield entry 56 >>> for entry in recscan('/some/path'): ... print(entry.name) ... foo/ foo/bar/ foo/bar/baz foo/qux PEP 380: Syntax for Delegating to a Subgenerator
  44. Генераторы def recscan(path): for entry in os.scandir(path): if entry.is_dir(): yield

    entry yield from recscan(entry.path) else: yield entry 57 >>> for entry in recscan('/some/path'): ... print(entry.name) ... foo/ foo/bar/ foo/bar/baz foo/qux PEP 380: Syntax for Delegating to a Subgenerator
  45. Генераторы def recscan(path): for entry in os.scandir(path): if entry.is_dir(): yield

    entry yield from recscan(entry.path) else: yield entry 58 >>> for entry in recscan('/some/path'): ... print(entry.name) ... foo/ foo/bar/ foo/bar/baz foo/qux PEP 380: Syntax for Delegating to a Subgenerator Автоматические next, send, throw и close
  46. Генераторы def recscan(path): count = 0 for entry in os.scandir(path):

    count += 1 if entry.is_dir(): yield entry count += yield from recscan(entry.path) else: yield entry return count 59 PEP 380: Syntax for Delegating to a Subgenerator
  47. Генераторы def recscan(path): count = 0 for entry in os.scandir(path):

    count += 1 if entry.is_dir(): yield entry count += yield from recscan(entry.path) else: yield entry return count 60 PEP 380: Syntax for Delegating to a Subgenerator Значение return или StopIteration
  48. Генераторы >>> gen = recscan('/some/path') >>> while True: ... try:

    ... entry = next(gen) ... print(entry.name) ... except StopIteration as e: ... print(e.value) ... break ... 62 PEP 380: Syntax for Delegating to a Subgenerator
  49. Генераторы >>> gen = recscan('/some/path') >>> while True: ... try:

    ... entry = next(gen) ... print(entry.name) ... except StopIteration as e: ... print(e.value) ... break ... foo/ foo/bar/ foo/bar/baz foo/qux 4 63 PEP 380: Syntax for Delegating to a Subgenerator
  50. Генераторы >>> gen = recscan('/some/path') >>> while True: ... try:

    ... entry = next(gen) ... print(entry.name) ... except StopIteration as e: ... print(e.value) ... break ... foo/ foo/bar/ foo/bar/baz foo/qux 4 64 PEP 380: Syntax for Delegating to a Subgenerator
  51. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 65 PEP 255: Simple Generators
  52. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 66 >>> tick_coro = tick() >>> tock_coro = tock() PEP 255: Simple Generators
  53. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 67 >>> tick_coro = tick() >>> tock_coro = tock() >>> next(tick_coro) Tick! PEP 255: Simple Generators
  54. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 68 >>> tick_coro = tick() >>> tock_coro = tock() >>> next(tick_coro) Tick! >>> next(tock_coro) Tock! PEP 255: Simple Generators
  55. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 69 >>> tick_coro = tick() >>> tock_coro = tock() >>> next(tick_coro) Tick! >>> next(tock_coro) Tock! >>> next(tick_coro) Tick! PEP 255: Simple Generators
  56. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 70 >>> tick_coro = tick() >>> tock_coro = tock() >>> next(tick_coro) Tick! >>> next(tock_coro) Tock! >>> next(tick_coro) Tick! >>> next(tock_coro) Tock! PEP 255: Simple Generators
  57. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 71 >>> tick_coro = tick() >>> tock_coro = tock() >>> next(tick_coro) Tick! >>> next(tock_coro) Tock! >>> next(tick_coro) Tick! >>> next(tock_coro) Tock! ... PEP 255: Simple Generators
  58. Корутины class Scheduler: def __init__(self): self.queue = deque() def add(self,

    callback): self.queue.append(callback) def run(self): while self.queue: callback = self.queue.popleft() callback() 72 PEP 255: Simple Generators
  59. Корутины class Scheduler: def __init__(self): self.queue = deque() def add(self,

    callback): self.queue.append(callback) def run(self): while self.queue: callback = self.queue.popleft() callback() 73 PEP 255: Simple Generators
  60. Корутины class Scheduler: def __init__(self): self.queue = deque() def add(self,

    callback): self.queue.append(callback) def run(self): while self.queue: callback = self.queue.popleft() callback() 74 PEP 255: Simple Generators
  61. Корутины class Scheduler: def __init__(self): self.queue = deque() def add(self,

    callback): self.queue.append(callback) def run(self): while self.queue: callback = self.queue.popleft() callback() 75 PEP 255: Simple Generators
  62. Корутины class Task: def __init__(self, coroutine, scheduler): self.coroutine = coroutine

    self.scheduler = scheduler self.schedule() def step(self): try: next(self.coroutine) except StopIteration: pass else: self.schedule() def schedule(self): self.scheduler.add(self.step) 76 PEP 255: Simple Generators
  63. Корутины class Task: def __init__(self, coroutine, scheduler): self.coroutine = coroutine

    self.scheduler = scheduler self.schedule() def step(self): try: next(self.coroutine) except StopIteration: pass else: self.schedule() def schedule(self): self.scheduler.add(self.step) 77 PEP 255: Simple Generators
  64. Корутины class Task: def __init__(self, coroutine, scheduler): self.coroutine = coroutine

    self.scheduler = scheduler self.schedule() def step(self): try: next(self.coroutine) except StopIteration: pass else: self.schedule() def schedule(self): self.scheduler.add(self.step) 78 PEP 255: Simple Generators
  65. Корутины class Task: def __init__(self, coroutine, scheduler): self.coroutine = coroutine

    self.scheduler = scheduler self.schedule() def step(self): try: next(self.coroutine) except StopIteration: pass else: self.schedule() def schedule(self): self.scheduler.add(self.step) 79 PEP 255: Simple Generators
  66. Корутины class Scheduler: def __init__(self): self.queue = deque() def add(self,

    callback): self.queue.append(callback) def run(self): while self.queue: callback = self.queue.popleft() callback() def create_task(self, coroutine): task = Task(coroutine, self) return task 80 PEP 255: Simple Generators
  67. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 81 PEP 255: Simple Generators
  68. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 82 >>> scheduler = Scheduler() PEP 255: Simple Generators
  69. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 83 >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) PEP 255: Simple Generators
  70. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 84 >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() PEP 255: Simple Generators
  71. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 85 >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! PEP 255: Simple Generators
  72. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 86 >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! Tock! PEP 255: Simple Generators
  73. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 87 >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! Tock! Tick! PEP 255: Simple Generators
  74. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 88 >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! Tock! Tick! Tock! PEP 255: Simple Generators
  75. Корутины def tick(): for _ in range(10): print('Tick!') yield def

    tock(): for _ in range(10): print('Tock!') yield 89 >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! Tock! Tick! Tock! ... PEP 255: Simple Generators
  76. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 90 PEP 255: Simple Generators
  77. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 91 PEP 255: Simple Generators
  78. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 92 PEP 255: Simple Generators
  79. Корутины class Task: def __init__(self, coroutine, scheduler): self.coroutine = coroutine

    self.stack = [] self.scheduler = scheduler self.schedule() ... 93 PEP 255: Simple Generators
  80. Корутины ... def step(self): try: yielded = next(self.coroutine) except StopIteration:

    if self.stack: self.coroutine = self.stack.pop() self.schedule() else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule() ... 94 PEP 255: Simple Generators
  81. Корутины ... def step(self): try: yielded = next(self.coroutine) except StopIteration:

    if self.stack: self.coroutine = self.stack.pop() self.schedule() else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule() ... 95 PEP 255: Simple Generators
  82. Корутины ... def step(self): try: yielded = next(self.coroutine) except StopIteration:

    if self.stack: self.coroutine = self.stack.pop() self.schedule() else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule() ... 96 PEP 255: Simple Generators
  83. Корутины ... def step(self): try: yielded = next(self.coroutine) except StopIteration:

    if self.stack: self.coroutine = self.stack.pop() self.schedule() else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule() ... 97 PEP 255: Simple Generators
  84. Корутины ... def step(self): try: yielded = next(self.coroutine) except StopIteration:

    if self.stack: self.coroutine = self.stack.pop() self.schedule() else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule() ... 98 PEP 255: Simple Generators
  85. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 99 PEP 255: Simple Generators
  86. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 100 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run()
  87. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 101 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick!
  88. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 102 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! <пауза в 1 сек>
  89. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 103 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! <пауза в 1 сек> Tock!
  90. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 104 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! <пауза в 1 сек> Tock! <пауза в 1 сек>
  91. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 105 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! <пауза в 1 сек> Tock! <пауза в 1 сек> Tick!
  92. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 106 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! <пауза в 1 сек> Tock! <пауза в 1 сек> Tick! <пауза в 1 сек>
  93. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 107 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! <пауза в 1 сек> Tock! <пауза в 1 сек> Tick! <пауза в 1 сек> Tock!
  94. Корутины def sleep(delay=0): d = time() + delay while True:

    yield if d <= time(): break def tick(): for _ in range(10): print('Tick!') yield sleep(2) def tock(): yield sleep(1) for _ in range(10): print('Tock!') yield sleep(2) 108 PEP 255: Simple Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Tick! <пауза в 1 сек> Tock! <пауза в 1 сек> Tick! <пауза в 1 сек> Tock! ...
  95. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 109 PEP 342: Coroutines via Enhanced Generators
  96. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 110 PEP 342: Coroutines via Enhanced Generators
  97. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 111 PEP 342: Coroutines via Enhanced Generators
  98. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 112 PEP 342: Coroutines via Enhanced Generators
  99. Корутины def step(self, value=None, error=None): try: if error: yielded =

    self.coroutine.throw(error) else: yielded = self.coroutine.send(value) except StopIteration as e: if self.stack: self.coroutine = self.stack.pop() self.schedule(value=e.value) except Exception as e: if self.stack: self.coroutine = self.stack.pop() self.schedule(error=e) else: raise else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule(value=yielded) 113 PEP 342: Coroutines via Enhanced Generators
  100. Корутины ... try: if error: yielded = self.coroutine.throw(error) else: yielded

    = self.coroutine.send(value) ... 115 PEP 342: Coroutines via Enhanced Generators
  101. Корутины ... except StopIteration as e: if self.stack: self.coroutine =

    self.stack.pop() self.schedule(value=e.value) ... 116 PEP 342: Coroutines via Enhanced Generators
  102. Корутины ... except Exception as e: if self.stack: self.coroutine =

    self.stack.pop() self.schedule(error=e) else: raise ... 117 PEP 342: Coroutines via Enhanced Generators
  103. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 120 PEP 342: Coroutines via Enhanced Generators
  104. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 121 PEP 342: Coroutines via Enhanced Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run()
  105. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 122 PEP 342: Coroutines via Enhanced Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick!
  106. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 123 PEP 342: Coroutines via Enhanced Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick! ...
  107. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 124 PEP 342: Coroutines via Enhanced Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick! ... Now yielding! Tick!
  108. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 125 PEP 342: Coroutines via Enhanced Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick! ... Now yielding! Tick! 45
  109. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = yield coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 126 PEP 342: Coroutines via Enhanced Generators >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick! ... Now yielding! Tick! 45 StopIteration: 45
  110. Корутины def step(self, value=None, error=None): try: if error: yielded =

    self.coroutine.throw(error) else: yielded = self.coroutine.send(value) except StopIteration as e: if self.stack: self.coroutine = self.stack.pop() self.schedule(value=e.value) except Exception as e: if self.stack: self.coroutine = self.stack.pop() self.schedule(error=e) else: raise else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule(value=yielded) 127 PEP 380: Syntax for Delegating to a Subgenerator
  111. Корутины def step(self, value=None, error=None): try: if error: yielded =

    self.coroutine.throw(error) else: yielded = self.coroutine.send(value) except StopIteration as e: if self.stack: self.coroutine = self.stack.pop() self.schedule(value=e.value) except Exception as e: if self.stack: self.coroutine = self.stack.pop() self.schedule(error=e) else: raise else: if inspect.isgenerator(yielded): self.stack.append(self.coroutine) self.coroutine = yielded self.schedule() else: self.schedule(value=yielded) 128 PEP 380: Syntax for Delegating to a Subgenerator
  112. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = \ yield from coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 129 PEP 380: Syntax for Delegating to a Subgenerator
  113. Корутины def step(self): try: self.coroutine.send(None) except StopIteration: pass else: self.schedule()

    def schedule(self): self.scheduler.add(self.step) 130 PEP 380: Syntax for Delegating to a Subgenerator
  114. Корутины def step(self): try: self.coroutine.send(None) except StopIteration: pass else: self.schedule()

    def schedule(self): self.scheduler.add(self.step) 131 PEP 380: Syntax for Delegating to a Subgenerator
  115. Корутины def step(self): try: self.coroutine.send(None) except StopIteration: pass else: self.schedule()

    def schedule(self): self.scheduler.add(self.step) 132 PEP 380: Syntax for Delegating to a Subgenerator
  116. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = \ yield from coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 133 PEP 380: Syntax for Delegating to a Subgenerator
  117. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = \ yield from coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 134 PEP 380: Syntax for Delegating to a Subgenerator >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run()
  118. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = \ yield from coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 135 PEP 380: Syntax for Delegating to a Subgenerator >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick!
  119. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = \ yield from coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 136 PEP 380: Syntax for Delegating to a Subgenerator >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick! ...
  120. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = \ yield from coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 137 PEP 380: Syntax for Delegating to a Subgenerator >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick! ... Now yielding! Tick!
  121. Корутины def coroutine(): result = 0 for number in range(10):

    result += number print('Now yielding!') yield return result def tick(): result = \ yield from coroutine() print(result) def tock(): for _ in range(10): print('Tick!') yield 138 PEP 380: Syntax for Delegating to a Subgenerator >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now yielding! Tick! ... Now yielding! Tick! 45
  122. Async/await async def coroutine(): result = 0 for number in

    range(10): result += number print('Now awaiting!') await sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): print('Tick!') await sleep(0) 139 PEP 492: Coroutines with async and await syntax
  123. Awaitable • Нативная корутина (async def); • Корутина-генератор, помеченная декоратором

    types.coroutine; • Объект с "магическим" методом __await__, возвращающим генератор. 143 PEP 492: Coroutines with async and await syntax
  124. Awaitable • Нативная корутина (async def); • Корутина-генератор, помеченная декоратором

    types.coroutine; • Объект с "магическим" методом __await__, возвращающим генератор. !!! Цепочка await / yield from всегда заканчивается простым yield. 144 PEP 492: Coroutines with async and await syntax
  125. Async/await @types.coroutine def sleep(delay=0): d = time.time() + delay while

    True: yield if d <= time.time(): break 145 PEP 492: Coroutines with async and await syntax
  126. Async/await class sleep: def __init__(self, delay=0): self.delay = delay def

    __await__(self): delay = self.delay d = time.time() + delay while True: yield if d <= time.time(): break 146 PEP 492: Coroutines with async and await syntax
  127. Async/await async def coroutine(): result = 0 for number in

    range(10): result += number print('Now awaiting!') await sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): print('Tick!') await sleep(0) 147 PEP 492: Coroutines with async and await syntax
  128. Async/await async def coroutine(): result = 0 for number in

    range(10): result += number print('Now awaiting!') await sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): print('Tick!') await sleep(0) 148 PEP 492: Coroutines with async and await syntax >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run()
  129. Async/await async def coroutine(): result = 0 for number in

    range(10): result += number print('Now awaiting!') await sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): print('Tick!') await sleep(0) 149 PEP 492: Coroutines with async and await syntax >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now awaiting! Tick!
  130. Async/await async def coroutine(): result = 0 for number in

    range(10): result += number print('Now awaiting!') await sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): print('Tick!') await sleep(0) 150 PEP 492: Coroutines with async and await syntax >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now awaiting! Tick! ...
  131. Async/await async def coroutine(): result = 0 for number in

    range(10): result += number print('Now awaiting!') await sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): print('Tick!') await sleep(0) 151 PEP 492: Coroutines with async and await syntax >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now awaiting! Tick! ... Now awaiting! Tick!
  132. Async/await async def coroutine(): result = 0 for number in

    range(10): result += number print('Now awaiting!') await sleep(0) return result async def tick(): result = await coroutine() print(result) async def tock(): for _ in range(10): print('Tick!') await sleep(0) 152 PEP 492: Coroutines with async and await syntax >>> scheduler = Scheduler() >>> scheduler.create_task(tick()) >>> scheduler.create_task(tock()) >>> scheduler.run() Now awaiting! Tick! ... Now awaiting! Tick! 45
  133. Выводы • Корутины = генераторы; • Корутины ≠ магия; •

    Корутины ≠ серебряная пуля; • asyncio проще, чем может показаться. 157