Reputation: 468
With send
and yield
we can have two-way communication with a generator and implement a state machine quite nicely (see example below). Now, we can't (?) send to asyncio coroutines, so how could one implement a state machine with asyncio-coroutines?
Generator example
def lock():
combination = [1, 2, 3]
for digit in combination:
a = (yield True)
while a != digit:
a = (yield False)
yield "You're in"
def main():
l = lock()
next(l)
assert l.send(2) == False
assert l.send(1) == True # correct value 1st digit
assert l.send(1) == False
assert l.send(2) == True # correct value 2nd digit
assert l.send(2) == False
assert l.send(3) == "You're in" # correct value 3rd digit
Something similar with asyncio is not quite as nice.. Is there a better way?
asyncio proposal
import asyncio
class AsyncLock:
class Message:
def __init__(self, value):
self.f = asyncio.Future()
self.value = value
def set_result(self, v):
self.f.set_result(v)
async def result(self):
return await self.f
def __init__(self, msg_q):
self.msg_q = msg_q
self.task = None
async def send(self, value):
msg = AsyncLock.Message(value)
await self.msg_q.put(msg)
return await msg.result()
# all of the above to be able to do this:
async def run(self):
combination = [1, 2, 3]
for digit in combination:
msg = await self.msg_q.get()
while msg.value != digit:
msg.set_result(False)
msg = await self.msg_q.get()
msg.set_result("You're in" if digit == 3 else True)
async def amain():
l = AsyncLock(asyncio.Queue())
l.task = asyncio.ensure_future(l.run())
assert await l.send(2) == False
assert await l.send(1) == True
assert await l.send(1) == False
assert await l.send(2) == True
assert await l.send(2) == False
assert await l.send(3) == "You're in"
asyncio.get_event_loop().run_until_complete(amain())
Upvotes: 3
Views: 1695
Reputation: 59553
Python3.6 added support for async generators (PEP525) so async
functions can now be generators too!
#!/usr/bin/env python3.6
import asyncio
async def lock():
combination = [1, 2, 3]
for digit in combination:
a = (yield True)
while a != digit:
a = (yield False)
yield "You're in!"
async def main():
coro = lock()
await coro.asend(None)
assert (await coro.asend(2)) == False
assert (await coro.asend(1)) == True
assert (await coro.asend(1)) == False
assert (await coro.asend(2)) == True
assert (await coro.asend(2)) == False
assert (await coro.asend(3)) == "You're in!"
print('Got it')
iol = asyncio.get_event_loop()
iol.run_until_complete(main())
Before Python3.6, the best approach is to use a message queue as you were doing.
Upvotes: 5