Python での一連の約束

Abdul Mateen 2023年6月21日
  1. Python での非同期プログラミング
  2. コールバック関数
  3. Python の try/except
  4. Python での一連の約束
Python での一連の約束

このチュートリアルでは、Python で一連の promise を作成する方法を説明します。 まず、Python での非同期プログラミングについて説明します。

次に、Python のコールバック関数について説明します。 最後に、実際のトピックに入る前に、Python での try/except について簡単に説明し、次に Python での一連の約束について説明します。

Python での非同期プログラミング

この記事では、オペレーティング システム スレッドの基本的な概念を理解していることを前提としています。 スレッドの予備知識がない場合は、前提条件として オペレーティング システムのスレッド を読むことができます。

非同期プログラミングでは、複数のスレッドを並行して実行できますが、メイン プログラム (通常はメイン/マネージャー スレッドと呼ばれます) は複数のワーカー スレッドを作成できます。 通常、メイン スレッドは、タスクの完了後にメイン スレッドに通知するワーカー スレッドを待機します。

通常のプログラミングとは異なり、競技まで制御を保持する代わりに、非同期関数は一時停止し、他の関数 (スレッド) を並行して実行できるようにします。

Python での非同期プログラミングについて説明し、例を示します。 ただし、関連する同期コードを最初に確認することをお勧めします。 このコードは、比較によって理解を深めるのに役立ちます。

def count():
    for i in range(5):
        print(i, end=" ")


def main():
    count()
    count()
    count()


main()

ここでは、count 関数を 3 回続けて呼び出します。 出来上がりは期待通り。

0 1 2 3 4 0 1 2 3 4 0 1 2 3 4

最初の count 関数の出力、続いて count 関数の 2 番目の呼び出しの出力、最後に最後の count 関数の出力が表示されます。

Python の asyncio ライブラリを使用すると、Python で非同期プログラムを実行できます。 非同期プログラミングの最初の要件は、関数を待機可能なオブジェクトとして設計することです。

標準関数を awaitable オブジェクトに変換するには、2つの要件があります。 1つ目は、async キーワードを (def キーワードの前に) 使用して、ルーチン関数の代わりに非同期関数を作成することです。

2 番目の要件は、非同期関数内で sleep 関数を呼び出し、現在の関数を中断し、他の関数を制御することです。

sleep ステートメントは、関数が正確にサスペンド状態になるコード内の特定のポイントです。 非同期プログラミングの 2 番目の要件は、待機可能なオブジェクト (非同期関数) を呼び出すときに await を追加することです。 そうしないと、エラーが発生します。

await キーワードは、イベント ループに現在の関数を一時停止して、他の関数に実行時間を与えるように指示します。

3つ目の要件は、gather 関数を呼び出して、待機可能なオブジェクト (非同期関数) を渡すことです。 gather 関数は、これらの関数を順番に実行しますが、同時に実行します。

これは、最初の関数が最初に開始され、しばらくすると、2 番目の関数も並行して開始されることを意味します。 同様に、すべての非同期関数が 1つずつ同時に実行を開始します。

では、コードを見てみましょう。

import asyncio


async def count():
    for i in range(5):
        print(i, end=" ")
        await asyncio.sleep(0.5)


async def main():
    await asyncio.gather(count(), count(), count())


if __name__ == "__main__":

    asyncio.run(main())

ここでは、以前のコードを非同期コードに変換し、特定の追加を行いました。 最初の行で、asyncio ライブラリがインポートされます。

async キーワードは、すべての関数の先頭に追加されます。

count 関数に sleep 関数呼び出しが追加されました。 await キーワードは、main 関数を含むすべての関数呼び出しに追加されます。

最後に、gather 関数が main で呼び出され、そこで count 関数が複数回呼び出され、各関数呼び出しが個別のスレッドとして示されます。

gather 関数を使用して、待機可能なオブジェクトを追加して、同時に実行する非同期関数のグループを形成します。 このコードの出力を見てみましょう。

0 0 0 1 1 1 2 2 2 3 3 3 4 4 4

出力では、すべてのスレッドが並列に実行され、完全に実行されるのではなく、非同期出力が生成されることがわかります。

同じ関数を複数回呼び出すと混乱する場合があります。 これは、異なる関数を並行して実行する別の例です。

import asyncio


async def count1():
    for i in range(10):
        print(i, end=" ")
        await asyncio.sleep(0.5)


async def count2():
    for i in range(50, 60):
        print(i, end=" ")
        await asyncio.sleep(0.5)


async def main():
    await asyncio.gather(count1(), count2())


asyncio.run(main())

このコードの出力は次のとおりです。

0 50 1 51 2 52 3 53 4 54 5 55 6 56 7 57 8 58 9 59

ここでも、両方の機能が同時に実行されています。

コールバック関数

コールバックは別の関数に (引数として) 渡されます。 他の関数は、その定義のどこかでこの関数をコールバックすることになっています。

ただし、呼び出しポイントは、別の関数がどのように定義されているかによって異なります。

ここに、コールバック関数に関連する簡単なコーディング例があります。

import random as r


def callback1(s):
    print(f"******* {s} *******")


def callback2(s):
    print(f"^^^^^^^ {s} ^^^^^^^")


def print_through_callback(message, f1, f2):
    if r.randint(0, 1) == 0:
        f1(message)
    else:
        f2(message)


def main():

    print_through_callback("Callback Example", callback1, callback2)


main()

このコードでは、print 関数に 3つのパラメーターがあります。 2 番目と 3 番目のパラメーターは関数名です。

main では、2つの関数を渡し、コードはランダムに 1つを呼び出します。 このコードを複数回実行すると、両方の関数がランダムに呼び出されることがわかります。

Python の try/except

Python は例外処理も提供します。 Python では、コードをテストするための try ブロックがあります。 例外の可能性があり、except ブロックで例外を処理できます。

ゼロ除算が定義されておらず、プログラム (ほぼすべてのプログラミング言語) がクラッシュすることは誰もが知っています。 ゼロ除算を呼び出すとき。 わからない場合は、このコードを試してください。

def main():
    x = int(input("Enter any number:"))
    print(2 / x)


main()

ゼロを入力して結果を確認します。 プログラムがクラッシュします。 コードのクラッシュは悪いことであり、例外処理を通じて回避する必要があります。

例外処理を含む同じコードを参照してください。

def main():
    try:
        x = int(input("Enter any number:"))
        print(2 / x)
    except:
        print("Divide by zero is not defined")


main()

このコードを実行してゼロ以外の値を入力する必要があります。 print (2/x) 内に書かれた除算演算の結果が得られます。 ゼロを入力すると、プログラムはクラッシュの代わりにゼロによる除算は定義されていませんというメッセージを表示します。

Python での一連の約束

コールバック関数は通常の関数と同じです。 ただし、その使用方法は異なります。

実行に時間がかかる重い関数を検討してください。 通常、このような関数は非同期に作成されます。

非同期関数はバックグラウンドで実行され、特定の時間が経過すると完了しますが、他の関数は並行して開始されます。 ただし、重い関数を実行した後に関数を実行したい場合は、コールバック関数を使用することを選択します。

ただし、そのようなタスクの完了には問題があります。 タスクが完了する前に例外をスローした場合はどうなりますか?

タスクが正常に完了した後に関数が確実に呼び出されるようにするには、promise と非同期プログラミングが必要です。

約束

promise は、非同期関数の成功または失敗 (失敗) 完了を表すオブジェクトです。

Promise オブジェクトは、非同期関数からの結果の値も表します。 promise は、複数のコールバックに関連する問題を管理するために使用されます。

promise API を使用して、Python で一連の promise を実行できます。 ただし、Python では async/await を使用して同じ目的を達成できます。

Python で非同期関数を実装するには、非同期関数で asyncio ライブラリを使用する必要があります。 上記ですでに説明した await キーワードを使用して、関数を順番に呼び出すことができます。

最後に、try/except ブロックを使用しています。 まず、コードと出力を見てください。

後で、try/except ブロックの目的について説明します。

import asyncio
import random as r


async def f1(x):
    await asyncio.sleep(1)
    return x ** 2


async def f2(x):
    await asyncio.sleep(1)
    return x / 2


async def f3(x):
    if r.randint(0, 1) == 0:
        return x
    raise ValueError(x)


async def run():
    try:
        value = await f3(await f2(await f1(r.randint(5, 9))))
    except ValueError as exception:
        print("Exception Occurred:", exception.args[0])
    else:
        print("No Exception:", value)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())
    loop.close()

以下は、上記の Python スクリプトを 5 回実行した結果をまとめたものです。

No Exception: 12.5
Exception Occurred: 12.5
Exception Occurred: 32.0
No Exception: 18.0
No Exception: 40.5

出力は、非同期操作で成功または失敗する可能性があることを示しているだけです。 したがって、try ブロック内の関数呼び出しの後に、コールバック関数のステートメントを配置できます。

正常に完了した場合、コードはコールバック関数を実行します。 失敗した場合、コントロールは except ブロックに移動し、コールバック関数を無視します。

このようにして、Python で一連の promise を処理できます。 正常に完了した場合は、コールバック関数を実行します (これは、必要なタスクの正常な完了に依存します)。 それ以外の場合は、コールバック関数を実行する必要はありません。