私はpython 3.6の非同期にかなり慣れていません
つまり、クラスがあり、そこにいくつかのプロパティを初期化したいということです。そして、プロパティの1つは、非同期関数からの戻り値です。
これを行うためのベストプラクティスは何ですか?
Init関数でevent_loopを1回呼び出して、戻り値を取得しますか?
__init__関数を非同期にしますか?イベントループで実行しますか?
乾杯!
以下は私のコードです:
import asyncio
import aioredis
from datetime import datetime
class C:
def __init__(self):
self.a = 1
self.b = 2
self.r = None
asyncio.get_event_loop().run_until_complete(self._async_init())
async def _async_init(self):
# this is the property I want, which returns from an async function
self.r = await aioredis.create_redis('redis://localhost:6379')
async def heart_beat(self):
while True:
await self.r.publish('test_channel', datetime.now().__str__())
await asyncio.sleep(10)
def run(self):
asyncio.get_event_loop().run_until_complete(self.heart_beat())
c=C()
c.run()
Init関数でevent_loopを1回呼び出して、戻り値を取得しますか?
___init__
_中にイベントループをスピンすると、イベントループの実行中にC
をインスタンス化できなくなります。 asyncioイベントループ ネストしないでください 。
[EDIT:質問の2回目の更新後、イベントループは非静的メソッド_C.run
_によって実行されるようです。 _run_until_complete
_の___init__
_は、記述されたコードで機能します。ただし、その設計には制限があります。たとえば、イベントループ中にC
またはC
のようなクラスのanotherインスタンスを構築することはできません。が走っています。]
_
__init__
_関数を非同期にしますか?イベントループで実行しますか?
___init__
_は、非常に醜いハックに頼らずに非同期にすることはできません。 Pythonの___init__
_は副作用によって動作し、None
を返す必要がありますが、_async def
_関数はコルーチンオブジェクトを返します。
これを機能させるには、いくつかのオプションがあります。
C
ファクトリ次のようなC
インスタンスを返す非同期関数を作成します。
_async def make_c():
c = C()
await c._async_init()
return c
_
このような関数は問題なく非同期であり、必要に応じて待機できます。関数よりも静的メソッドを好む場合、またはクラスで定義されていない関数からプライベートメソッドにアクセスすることに不快感を感じる場合は、make_c()
をC.create()
に置き換えることができます。
C.r
_フィールドr
をその中に格納するだけで、Future
プロパティを非同期にすることができます。
_class C:
def __init__(self):
self.a = 1
self.b = 2
loop = asyncio.get_event_loop()
# note: no `await` here: `r` holds an asyncio Task which will
# be awaited (and its value accessed when ready) as `await c.r`
self.r = loop.create_task(aioredis.create_redis('redis://localhost:6379'))
_
これには、すべての_c.r
_の使用を_await c.r
_と綴る必要があります。それが許容できる(または有益でさえある)かどうかは、プログラムの他の場所で使用される場所と頻度によって異なります。
C
コンストラクター___init__
_を非同期にすることはできませんが、この制限はその低レベルのいとこ___new__
_には適用されません。 _T.__new__
_は、T
のインスタンスでさえないオブジェクトを含む、任意のオブジェクトを返す可能性があります。これは、コルーチンオブジェクトを返すために使用できる事実です。
_class C:
async def __new__(cls):
self = super().__new__(cls)
self.a = 1
self.b = 2
self.r = await aioredis.create_redis('redis://localhost:6379')
return self
# usage from another coroutine
async def main():
c = await C()
# usage from outside the event loop
c = loop.run_until_complete(C())
_
この最後のアプローチは、それを使用する本当に正当な理由がない限り、本番コードにはお勧めしません。
C
インスタンスを返すことを気にしない_C.__new__
_コンストラクターを定義するため、これはコンストラクターメカニズムの悪用です。C.__init__
_の呼び出しを拒否します。await C()
の使用は、非同期に慣れている人にとってさえ(または特に)非常に非慣用的に見えます。