

# Python の Lambda 関数ハンドラーの定義
<a name="python-handler"></a>

Lambda 関数*ハンドラー*は、イベントを処理する関数コード内のメソッドです。関数が呼び出されると、Lambda はハンドラーメソッドを実行します。関数は、ハンドラーが応答を返すか、終了するか、タイムアウトするまで実行されます。

このページでは、命名規則、有効なハンドラー署名、コードのベストプラクティスなど、Python で Lambda 関数ハンドラーを操作する方法について説明します。このページには、注文に関する情報を取得し、テキストファイル受信を生成し、このファイルを Amazon Simple Storage Service (Amazon S3) バケットに配置する Python Lambda 関数の例も含まれています。

**Topics**
+ [Python Lambda 関数のコードの例](#python-handler-example)
+ [ハンドラーの命名規則](#python-handler-naming)
+ [Lambda のイベントオブジェクトの使用](#python-handler-event)
+ [Lambda コンテキストオブジェクトへのアクセスと使用](#python-handler-context)
+ [Python ハンドラーの有効なハンドラー署名](#python-handler-signature)
+ [値の返し](#python-handler-return)
+ [ハンドラーでの AWS SDK for Python (Boto3) の使用](#python-handler-sdk)
+ [環境変数にアクセスする](#python-handler-env-vars)
+ [Python Lambda 関数のベストプラクティスに従ってください。](#python-handler-best-practices)

## Python Lambda 関数のコードの例
<a name="python-handler-example"></a>

以下の Python Lambda 関数 Lambda 関数コードの例では、注文に関する情報を取得し、テキストファイル受信を生成し、このファイルを Amazon S3 バケットに配置します。

**Example Python Lambda 関数**  

```
import json
import os
import logging
import boto3

# Initialize the S3 client outside of the handler
s3_client = boto3.client('s3')

# Initialize the logger
logger = logging.getLogger()
logger.setLevel("INFO")

def upload_receipt_to_s3(bucket_name, key, receipt_content):
    """Helper function to upload receipt to S3"""
    
    try:
        s3_client.put_object(
            Bucket=bucket_name,
            Key=key,
            Body=receipt_content
        )
    except Exception as e:
        logger.error(f"Failed to upload receipt to S3: {str(e)}")
        raise

def lambda_handler(event, context):
    """
    Main Lambda handler function
    Parameters:
        event: Dict containing the Lambda function event data
        context: Lambda runtime context
    Returns:
        Dict containing status message
    """
    try:
        # Parse the input event
        order_id = event['Order_id']
        amount = event['Amount']
        item = event['Item']
        
        # Access environment variables
        bucket_name = os.environ.get('RECEIPT_BUCKET')
        if not bucket_name:
            raise ValueError("Missing required environment variable RECEIPT_BUCKET")

        # Create the receipt content and key destination
        receipt_content = (
            f"OrderID: {order_id}\n"
            f"Amount: ${amount}\n"
            f"Item: {item}"
        )
        key = f"receipts/{order_id}.txt"

        # Upload the receipt to S3
        upload_receipt_to_s3(bucket_name, key, receipt_content)

        logger.info(f"Successfully processed order {order_id} and stored receipt in S3 bucket {bucket_name}")
        
        return {
            "statusCode": 200,
            "message": "Receipt processed successfully"
        }

    except Exception as e:
        logger.error(f"Error processing order: {str(e)}")
        raise
```

この ファイルには以下のコードのセクションが含まれます:
+ `import` ブロック: このブロックを使用して、Lambda 関数に必要なライブラリを含めます。
+ SDK クライアントとロガーのグローバル初期化: ハンドラーの外側に初期化コードを含めると、[実行環境](lambda-runtime-environment.md)の再利用を活用して関数のパフォーマンスが向上します。詳細については、「[Python Lambda 関数のベストプラクティスに従ってください。](#python-handler-best-practices)」を参照してください。
+ `def upload_receipt_to_s3(bucket_name, key, receipt_content):` これは、メイン `lambda_handler` 関数によって呼び出されるヘルパー関数です。
+ `def lambda_handler(event, context):`: これは**メインハンドラー関数**で、メインアプリケーションロジックが含まれています。Lambda が関数ハンドラーを呼び出すと、[Lambda ランタイム](concepts-basics.md#gettingstarted-concepts-runtime)は 2 つの引数を関数に渡します。1 つは関数が処理するデータを含む[イベントオブジェクト](#python-handler-event)で、もう 1 つは関数に関する情報が含まれる[コンテキストオブジェクト](#python-handler-context)です。

## ハンドラーの命名規則
<a name="python-handler-naming"></a>

Lambda 関数の作成時に定義される関数ハンドラー名は、以下から取得されます。
+ Lambda ハンドラー関数が配置されているファイルの名前
+ Python ハンドラー関数の名前

上記の例では、ファイルが `lambda_function.py` という名前の場合、ハンドラーは `lambda_function.lambda_handler` として指定されます。これは、Lambda コンソールを使用して作成する関数にデフォルトで付けられるハンドラー名です。

異なるファイル名または関数ハンドラー名を使用してコンソールで関数を作成する場合は、デフォルトのハンドラー名を編集する必要があります。

**関数ハンドラー名を変更するには (コンソール)**

1. Lambda コンソールの [[関数]](https://console.aws.amazon.com/lambda/home#/functions) ページを開き、関数を選択します。

1. **[コード]** タブを選択します。

1. **[ランタイム設定]** ペインまでスクロールして、**[編集]** を選択します。

1. **[ハンドラー]** には、関数ハンドラーの新しい名前を入力します。

1. **[保存]** を選択します。

## Lambda のイベントオブジェクトの使用
<a name="python-handler-event"></a>

Lambda が関数を呼び出すとき、[イベントオブジェクト](concepts-basics.md#gettingstarted-concepts-event)引数が関数ハンドラーに渡されます。JSON オブジェクトは、Lambda 関数で最も一般的なイベント形式です。前のセクションのコード例では、関数は次の形式の入力を期待します。

```
{
    "Order_id": "12345",
    "Amount": 199.99,
    "Item": "Wireless Headphones"
}
```

関数が他の AWS のサービス によって呼び出された場合、入力イベントも JSON オブジェクトです。イベントオブジェクトの具体的な形式は、関数を呼び出すサービスによって異なります。特定のサービスのイベント形式を確認するには、[他の AWS サービスからのイベントを使用した Lambda の呼び出し](lambda-services.md) 章の該当するページを参照してください。

入力イベントが JSON オブジェクトの形式の場合、Lambda ランタイムはオブジェクトを Python 辞書型に変換します。入力 JSON の値をコードの変数に割り当てるには、コード例のように標準の Python の辞書メソッドを使用します。

また、関数に JSON 配列や他の有効な JSON データ型をデータとして渡すこともできます。次の表は、Python ランタイムが JSON 型を変換する際の対応関係を示します。


| JSON データ型 | Python データ型 | 
| --- | --- | 
| オブジェクト | 辞書 (dict) | 
| 配列 | リスト (list) | 
| 数値 | 整数 (int) または浮動小数点数 (float） | 
| 文字列 | 文字列 (str) | 
| ブール値 | ブール値 (bool) | 
| null | NoneType (NoneType） | 

## Lambda コンテキストオブジェクトへのアクセスと使用
<a name="python-handler-context"></a>

Lambda コンテキストオブジェクトには、関数の呼び出しと実行環境に関する情報が含まれています。Lambda は、関数が呼び出されると、自動的にコンテキストオブジェクトを渡します。モニタリング目的として、コンテキストオブジェクトを使用して関数の呼び出しに関する情報を出力できます。

コンテキストオブジェクトは、[Lambda ランタイムインターフェイスクライアント](https://github.com/aws/aws-lambda-python-runtime-interface-client/blob/main/awslambdaric/lambda_context.py)で定義された Python クラスです。コンテキストオブジェクトのプロパティのいずれかの値を返すには、コンテキストオブジェクトの対応するメソッドを使用します。例えば、次のコードスニペットは、 `aws_request_id` プロパティ (呼び出しリクエストの識別子) の値を `request` という名前の変数に割り当てます。

```
request = context.aws_request_id
```

Lambda コンテキストオブジェクトの使用方法の詳細と、使用可能なメソッドとプロパティの完全なリストについては、「[Lambda コンテキストオブジェクトを使用して Python 関数の情報を取得する](python-context.md)」を参照してください。

## Python ハンドラーの有効なハンドラー署名
<a name="python-handler-signature"></a>

Python でハンドラー関数を定義する場合、関数は 2 つの引数を取るようにしてください。これらの引数のうち、1 番目の引数は Lambda [イベントオブジェクト](#python-handler-event)で、2 番目の引数は Lambda [コンテキストオブジェクト](#python-handler-context)です。慣例により、これらの入力引数には通常 `event` と `context` という名前が付けられますが、任意の名前を付けることができます。入力引数を １ つでハンドラー関数を宣言すると、Lambda は関数の実行をしようとするとエラーになります。Python でハンドラー関数を宣言する最も一般的な方法を次に示します。

```
def lambda_handler(event, context):
```

次の例に示すように、関数の宣言で Python の型ヒントを使用することもできます。

```
from typing import Dict, Any
      
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
```

他の `aws-lambda-typing` によって生成されたイベントやコンテキストオブジェクトに特定の AWS のサービス 型を使用するには、AWS パッケージを関数のデプロイメントパッケージに追加します。このライブラリは、`pip install aws-lambda-typing` を実行して開発環境にインストールできます。次のコードスニペットは、AWS に固有の型ヒントの使用方法を示しています。この例では、期待されるイベントは Amazon S3 イベントです。

```
from aws_lambda_typing.events import S3Event
from aws_lambda_typing.context import Context
from typing import Dict, Any

def lambda_handler(event: S3Event, context: Context) -> Dict[str, Any]:
```

ハンドラー `async` 関数に Python 関数型を使用することはできません。

## 値の返し
<a name="python-handler-return"></a>

オプションで、ハンドラーは値を返すことができますが、その値はJSONにシリアライズできるものでなければなりません。一般的な戻り値の型には、 `dict`、`list`、`str`、`int`、`float`、`bool` があります。

戻り値に何が起きるかは、[呼び出しの種類](lambda-invocation.md)と呼び出した[サービス](lambda-services.md) に応じて変わります。例:
+ `RequestResponse` 呼び出しタイプを使用して [Lambda 関数を同期的に呼び出す](invocation-sync.md)場合、Lambda は Python 関数の呼び出しの結果を、Lambda 関数を呼び出したクライアントに返します (呼び出しリクエストに対する HTTP レスポンスでは、JSON にシリアル化されます)。たとえば、AWS Lambda のコンソールは `RequestResponse` の呼び出しタイプを使用するため、コンソールを使用して関数を呼び出すと、コンソールに戻り値が表示されます。
+ ハンドラーから `json.dumps` でシリアル化できないオブジェクトが返された場合、ランタイムはエラーを返します。
+ `None` ステートメントを指定しなかった場合の Python 関数の暗黙の動作と同じように、ハンドラーが `return` を返した場合、ランタイムは `null` を返します。
+ 「`Event`」 呼び出しタイプ ([非同期呼び出し](invocation-async.md)) を使用する場合、値は破棄されます。

サンプルコードでは、ハンドラーは次の Python の辞書を返します。

```
{
  "statusCode": 200,
  "message": "Receipt processed successfully"
}
```

Lambda ランタイムはこの辞書をシリアル化し、関数を呼び出したクライアントに JSON 文字列として返します。

**注記**  
Python 3.9 以降のリリースでは、Lambda はエラーの応答時に呼び出しの requestId を含めます。

## ハンドラーでの AWS SDK for Python (Boto3) の使用
<a name="python-handler-sdk"></a>

多くの場合、Lambda 関数を使用して、他の AWS のサービス やリソースとやり取りしたりします。これらのリソースとインターフェイスする最も簡単な方法は、AWS SDK for Python (Boto3) を使用することです。[サポートされているすべての Lambda Python ランタイム](lambda-runtimes.md#runtimes-supported)には、SDK for Python のバージョンが含まれています。ただし、コードが SDK の利用を必要とする場合、関数デプロイパッケージに SDK を含めることを強く推奨します。デプロイパッケージに SDK を含めると、依存関係を完全に制御でき、他のライブラリとのバージョン不整合の問題のリスクが軽減できます。詳細については、「[Python でのランタイム依存関係](python-package.md#python-package-dependencies)」および「[下位互換性](runtimes-update.md#runtime-update-compatibility)」を参照してください。

Lambda 関数で SDK for Python を使用するには、関数コードの先頭にある import ブロックに次のステートメントを追加します。

```
import boto3
```

`pip install` コマンドを使用して、`boto3` ライブラリを関数のデプロイメント パッケージに追加します。.zip デプロイパッケージに依存関係を追加する方法の詳細については、「[依存関係を含めて .zip デプロイパッケージを作成する](python-package.md#python-package-create-dependencies)」を参照してください。コンテナイメージとしてデプロイされた Lambda 関数への依存関係の追加の詳細については、「[ベースイメージからイメージを作成する](python-image.md#python-image-create)」または「[代替ベースイメージからイメージを作成する](python-image.md#python-alt-create)」を参照してください。

コードで `boto3` を使用する場合、クライアントを初期化するための認証情報を提供する必要はありません。例えば、サンプルコードでは、Amazon S3クライアントを初期化するために次のコード行を使用します。

```
# Initialize the S3 client outside of the handler
s3_client = boto3.client('s3')
```

Python では、Lambda が認証情報を使用して環境変数を自動的に作成します。`boto3` SDK は、初期化の際にこれらの認証情報に関して関数内の環境変数をチェックします。

## 環境変数にアクセスする
<a name="python-handler-env-vars"></a>

ハンドラーコードでは、`os.environ.get` メソッドを使用して[環境変数](configuration-envvars.md)を参照できます。このサンプルコードでは、以下のコード行を使用して、定義された `RECEIPT_BUCKET` 環境変数を参照します:

```
# Access environment variables
bucket_name = os.environ.get('RECEIPT_BUCKET')
```

コードの先頭にある import ブロックに `import os` ステートメントを含めることを忘れないでください。

## Python Lambda 関数のベストプラクティスに従ってください。
<a name="python-handler-best-practices"></a>

Lambda 関数をビルドするときは、次のリストのガイドラインに従って、コーディングのベストプラクティスを実行してください。
+ **Lambda ハンドラーをコアロジックから分離します。**これにより、関数の単体テストが実行しやすくなります。例えば、Python では次のようになります。

  ```
  def lambda_handler(event, context):
      foo = event['foo']
      bar = event['bar']      
      result = my_lambda_function(foo, bar)
  
  def my_lambda_function(foo, bar):
      // MyLambdaFunction logic here
  ```
+ **関数のデプロイパッケージ内で依存関係を制御します。**AWS Lambda 実行環境には多数のライブラリが含まれています。Node.js および Python ランタイムの場合、ライブラリには AWS SDK が含まれます。最新の機能やセキュリティ更新プログラムを有効にするために、Lambda はこれらのライブラリを定期的に更新します。この更新により、Lambda 関数の動作が微妙に変化する場合があります。関数で使用する依存関係を完全に制御するには、すべての依存関係をデプロイパッケージでパッケージングします。
+ **依存関係の複雑さを最小限に抑えます。**フレームワークを単純化して、[実行環境](lambda-runtime-environment.md)起動時のロードを高速化します。
+ **デプロイパッケージをランタイムに必要な最小限のサイズにします。**これにより、呼び出しに先立ってデプロイパッケージをダウンロードして解凍する所要時間が短縮されます。

**実行環境の再利用を活用して関数のパフォーマンスを向上させます。**関数ハンドラー外で SDK クライアントとデータベース接続を初期化し、静的なアセットを `/tmp` ディレクトリにローカルにキャッシュします。関数の同じインスタンスで処理された後続の呼び出しは、これらのリソースを再利用できます。これにより、関数の実行時間が短縮され、コストが節約されます。

呼び出し間でデータが漏れるのを防ぐため、実行環境を使用してセキュリティ上の懸念があるユーザーデータ、イベント、またはその他の情報を保存しないでください。関数がハンドラー内のメモリに保存できない変更可能な状態に依存している場合は、ユーザーごとに個別の関数または個別のバージョンの関数を作成することを検討してください。

**keep-alive ディレクティブを使用して永続的な接続を維持します。**Lambda は、時間の経過とともにアイドル状態の接続を消去します。関数を呼び出すときにアイドル状態の接続を再利用しようとすると、接続エラーが発生します。永続的な接続を維持するには、ランタイムに関連付けられている keep-alive ディレクティブを使用します。例については、「[Node.js で Keep-alive を使用して接続を再利用する](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html)」を参照してください。

**[環境変数](configuration-envvars.md)を使用して、オペレーショナルパラメータを関数に渡します。**たとえば、Amazon S3 バケットに書き込む場合、書き込み先のバケット名はハードコーディングせずに、環境変数として設定します。

Lambda 関数では、**再帰呼び出しを使用しないでください**。関数が自身を呼び出すこともあれば、新たに開始されたプロセスで関数が再度呼び出される可能性もあります。これを行うと意図しないボリュームで関数が呼び出され、料金が急増する可能性があります。意図しない呼び出しがいくつも見つかった場合は、すぐに関数の予約済同時実行数を `0` に設定して、コードを更新している間のすべての関数の呼び出しをスロットリングします。

Lambda 関数コードで**文書化されていない非公開の API を使用しないでください**。AWS Lambda マネージドランタイムでは、Lambda が Lambda の内部 API にセキュリティと機能面の更新を定期的に適用します。これらの内部 API 更新には後方互換性がないことがあり、関数にこれらの非公開 API に対する依存関係がある場合、呼び出しの失敗などの意図しない結果につながります。公開されている API のリストについては、「[API リファレンス](https://docs.aws.amazon.com/lambda/latest/api/welcome.html)」を参照してください。

**冪等性コードを記述します。**関数の記述に冪等性コードを使用すると、重複するイベントが同じ方法で処理されるようになります。コードでは、イベントを適切に検証し、重複するイベントを適切に処理する必要があります。詳細については、「[Lambda 関数を冪等にするにはどうすればよいですか?](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-idempotent/)」を参照してください。