Pythonのasync/await。入門するとエラーがわかりづらい。
- SyntaxError: 'await' outside function
- RuntimeWarning: coroutine 'main' was never awaited
- TypeError: object async_generator can't be used in 'await' expression
- TypeError: object generator can't be used in 'await' expression
- await value
<generator object 関数名 at アドレス>
がエターナル - await value
<async_generator object 関数名 at アドレス>
がエターナル - TypeError: 'async_generator' object is not iterable
- TypeError: 'async for' requires an object with
__aiter__
method, got generator - RuntimeWarning: coroutine 'sleep' was never awaited
- RuntimeError: Task got bad yield: 0
そこで上記のエラーが出るシンプルなコード例(エラーの出ない例も少し)を作ってみることにしました。
この記事には、本質的な同期/非同期の話はありません。単にasync/await周りのエラーはこうすると出ますという、逆引き風の例です。
余談ですが、この例を作り終わったあと、昨晩ハマっていたいくつかのasync/await課題はコードを見ただけでウソのように直せるようになっていました。
SyntaxError: 'await' outside function
構文エラーで、「"await" は非同期関数内でのみ許可されます」というPylanceのメッセージが出ます。
エラー例1
import asyncio
async def simple():
return 49
await simple()
エラー例2
import asyncio
async def simple():
return 49
def main(): # ★これが非同期関数ではない
ret_value = await simple()
print(f"await value {ret_value}")
main()
main()をasync defにして実行すると次のRuntimeWarningが出ます。 asyncのコルーチンは関数のように呼び出しても実行されないのがPythonのasync/awaitのわかりにくいところですね。コルーチンを生成してくれるだけ(コンストラクタ)です。
RuntimeWarning: coroutine 'main' was never awaited
これはワーニングが出る例ですが、プログラムとして実行もされません。
ワーニング例
import asyncio
async def simple():
return 49
async def main():
ret_value = await simple()
print(f"await value {ret_value}")
main() # 非同期関数の呼び出しはコルーチンを生成するだけ、実行されない。
動く例
import asyncio
async def simple():
return 49
async def main():
ret_value = await simple()
print(f"await value {ret_value}")
asyncio.run(main()) # 非同期関数を実行する。
TypeError: object async_generator can't be used in 'await' expression
エラー例
import asyncio
async def simple(): ## async付きでyieldする関数は非同期ジェネレータ
for i in range(10):
print(f"good_yield({i})")
yield i
async def main():
ret_value = await simple()
print(f"await value {ret_value}")
asyncio.run(main())
非同期ジェネレータはawaitしてもダメです。
ジェネレータ(generator)は、for文で呼び出すのが普通です。
for value in <ジェネレータ>:
valueを使う。
世界的に有名なジェネレータは range() でしょうか。
TypeError: object generator can't be used in 'await' expression
先のものと似ていますが、 simple()は async_generator ではなく、普通の generator です。
エラー例
import asyncio
def simple(): # これは普通のジェネレータ
for i in range(10):
print(f"good_yield({i})")
yield i
async def main():
ret_value = await simple()
print(f"await value {ret_value}")
asyncio.run(main())
await value <generator object 関数 at アドレス>
がエターナル
エターナル例
import asyncio
def simple(): # yieldで戻る関数はジェネレータオブジェクト(Generator)
for i in range(10):
print(f"good_yield({i})")
yield i
async def main():
while True:
ret_value = simple() # これはジェネレータを生成しているだけ
print(f"await value {ret_value}")
asyncio.run(main())
ちなみにジェネレータは先に書いた通り、for文で使います。
for value in sample():
print(value)
await value <async_generator object 関数名 at アドレス>
がエターナル
先の例のgeneratorがasync_generatorになっただけです。
エターナル例
import asyncio
async def simple(): # さっきの例にasyncを入れただけ。async_generatorに変わった
for i in range(10):
print(f"good_yield({i})")
yield i
async def main():
while True:
ret_value = simple()
print(f"await value {ret_value}")
asyncio.run(main())
非同期ジェネレータはasync forで使います。
async for value in sample():
print(value)
TypeError: 'async_generator' object is not iterable
非同期ジェネレータオブジェクトがiterableではない。。。ジェネレータがiterableではないというメッセージはちょっと理不尽な気もしますね。
エラー例
import asyncio
async def simple(): # ★ここをasyncにした。
for i in range(10):
print(f"good_yield({i})")
yield i
async def main():
for ret_value in simple(): # async forにしないと理不尽なことを言われる。
print(f"await value {ret_value}")
asyncio.run(main())
理不尽でない例
import asyncio
async def simple():
for i in range(10):
print(f"good_yield({i})")
yield i
async def main():
async for ret_value in simple(): # ここにasyncを入れる
print(f"await value {ret_value}")
asyncio.run(main())
TypeError: 'async for' requires an object with __aiter__
method, got generator
今度は async for で普通のジェネレータを呼んでしまった場合。 普通ジェネレータには__iter__
が、非同期ジェネレータには__aiter__
メソッドが付きます。
エラー例
import asyncio
def simple(): # ここのasyncを外すと __iter__ はあるけど __aiter__ がない。
for i in range(10):
print(f"good_yield({i})")
yield i
async def main():
async for ret_value in simple(): # ここにasyncがあるため要 __aiter__
print(f"await value {ret_value}")
asyncio.run(main())
RuntimeWarning: coroutine 'sleep' was never awaited
asyncio.sleep()に awaitを忘れた場合(sleepしてくれない)。
ワーニング例
import asyncio
async def whild_simple():
i = 0
while True:
i += 1
print(f"while simple {i}")
asyncio.sleep(1) # ここにawaitが抜けている
yield i
async def main():
async for ret_value in whild_simple():
print(f"main ret_value = {ret_value}")
asyncio.run(main())
RuntimeError: Task got bad yield: 0
ジェネレータをtaskで実行しちゃった場合
エラー例
import asyncio
import time
def whild_simple(): # 普通のジェネレータ
i = 0
while True:
time.sleep(1)
i += 1
if i % 2 == 0:
print(f"while yield {i}")
yield i
else:
print(f"while not yield {i}")
async def main():
task = asyncio.create_task(whild_simple()) # taskとして実行
await task
asyncio.run(main())
動く例
import asyncio
import time
async def while_simple():
i = 0
while True:
time.sleep(1)
i += 1
if i % 2 == 0:
print(f"while yield {i}")
yield i
else:
print(f"while not yield {i}")
async def main():
async for value in while_simple(): # ジェネレータは async for で呼ぶ
print(value)
asyncio.run(main())
TypeError: a coroutine was expected, got <async_generator object 関数 at アドレス>
非同期ジェネレータをtaskとして実行しちゃった場合
エラー例
import asyncio
import time
async def while_simple(): # 非同期ジェネレータ
i = 0
while True:
time.sleep(1)
i += 1
if i % 2 == 0:
print(f"while yield {i}")
yield i
else:
print(f"while not yield {i}")
async def main():
task = asyncio.create_task(while_simple()) # 非同期ジェネレータをtaskで実行
await task
asyncio.run(main())
以上。お疲れ様でした。