同期アプリケーションと非同期アプリケーションの両方で使用するクラスを実装するとき、私は両方の使用例で実質的に同じコードを維持していることに気づきました。
例として、次のことを考慮してください。
from time import sleep
import asyncio
class UselessExample:
def __init__(self, delay):
self.delay = delay
async def a_ticker(self, to):
for i in range(to):
yield i
await asyncio.sleep(self.delay)
def ticker(self, to):
for i in range(to):
yield i
sleep(self.delay)
def func(ue):
for value in ue.ticker(5):
print(value)
async def a_func(ue):
async for value in ue.a_ticker(5):
print(value)
def main():
ue = UselessExample(1)
func(ue)
loop = asyncio.get_event_loop()
loop.run_until_complete(a_func(ue))
if __name__ == '__main__':
main()
この例では、それほど悪くはありません。ticker
のUselessExample
メソッドは、タンデムで維持するのは簡単ですが、例外処理とより複雑な機能がメソッドをすばやく成長させ、それを作成できると想像できます。どちらの方法も実質的に同じままでかまいませんが、より多くの問題があります(特定の要素を非同期の対応するものに置き換えるだけです)。
両方を完全に実装する価値がある実質的な違いがないと仮定すると、このようなクラスを維持し、不必要な重複を回避する最良の(そして最もPythonicな)方法は何ですか?
_async/await
_は設計上感染力があります。
コードには同期ユーザーと非同期ユーザーがあり、これらのユーザーには実装が異なるため、異なる要件があることを受け入れます。
個別のライブラリを公開する
たとえば、aiohttp
と_aiohttp-requests
_とrequests
を比較します。
同様に、asyncpg
と_psycopg2
_を比較します。
そこに着く方法
オプション1。 (簡単)クローンの実装。分岐を許可します。
Opt2。 (賢明な)部分的なリファクタリング、例えば非同期ライブラリは、同期ライブラリに依存してインポートします。
Opt3。 (ラジカル)同期プログラムと非同期プログラムの両方で使用できる「純粋な」ライブラリを作成します。たとえば、 https://github.com/python-hyper/hyper-h2 を参照してください。
良い面としては、テストがより簡単で徹底的です。テストフレームワークが非同期プログラムで考えられるすべての同時実行順序を強制的に評価するのがどれほど難しい(または不可能な)かを検討してください。純粋なライブラリはそれを必要としません:)
マイナス面としては、このプログラミングスタイルは別の考え方を必要とし、常に単純であるとは限りません。たとえば、await socket.read(2**20)
の代わりにfor event in fsm.Push(data): ...
を記述し、ライブラリユーザーが適切なサイズのチャンクでデータを提供するようにします。
コンテキストについては、 https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/ のbackpressure
引数を参照してください