Доклад будет посвящен механизму работы корутин в ЯП Python: какое отношение к ним имеют генераторы, как генераторы превратить в корутины и как реализовать простейший event-loop для асинхронного выполнения корутин.
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!')
приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. 17
приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. • Обобщенные подпрограммы; 18
приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. • Обобщенные подпрограммы; • Позволяют реализовать кооперативную многозадачность. 19
производительности; • В CPU-bound задачах не быстрее последовательного выполнения; ◦ ThreadPool или ProcessPool; • Большой прирост производительности в IO-bound задачах. 27
производительности; • В CPU-bound задачах не быстрее последовательного выполнения; ◦ ThreadPool или ProcessPool; • Большой прирост производительности в IO-bound задачах. 28 Возможно реализовать схему "Одна корутина — один поток".
приостановлена в произвольной точке; ◦ сохраняет свое состояние; ◦ передает управление другой корутине; ◦ может быть продолжена с точки приостановки. • Обобщенные подпрограммы; • Позволяют реализовать кооперативную многозадачность. 29
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
index index += 1 except ValueError: print('Oh!') 43 >>> gen = generator() >>> next(gen) 0 >>> gen.throw(ValueError) Oh! 0 PEP 342: Coroutines via Enhanced Generators
index index += 1 except GeneratorExit: print('Oh!') 53 >>> gen = generator() >>> next(gen) 0 >>> del gen Oh! PEP 342: Coroutines via Enhanced Generators
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
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
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
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
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
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
... entry = next(gen) ... print(entry.name) ... except StopIteration as e: ... print(e.value) ... break ... 62 PEP 380: Syntax for Delegating to a Subgenerator
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
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
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
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
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
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
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
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
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
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()
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!
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! ...
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!
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
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
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
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
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()
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!
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! ...
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!
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
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
types.coroutine; • Объект с "магическим" методом __await__, возвращающим генератор. !!! Цепочка await / yield from всегда заканчивается простым yield. 144 PEP 492: Coroutines with async and await syntax
__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
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
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!
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!
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