

# 定义采用 Python 的 Lambda 函数处理程序
<a name="python-handler"></a>

Lambda 函数*处理程序*是函数代码中处理事件的方法。当调用函数时，Lambda 运行处理程序方法。您的函数会一直运行，直到处理程序返回响应、退出或超时。

本页介绍如何使用采用 Python 的 Lambda 函数处理程序，包括命名约定、有效的处理程序签名和代码最佳实践。本页还包括 Python Lambda 函数的示例，该函数接收订单信息，生成文本文件收据，然后将此文件放入 Amazon Simple Storage Service（Amazon S3）存储桶中。

**Topics**
+ [

## 示例 Python Lambda 函数代码
](#python-handler-example)
+ [

## 处理程序命名约定
](#python-handler-naming)
+ [

## 使用 Lambda 事件对象
](#python-handler-event)
+ [

## 访问和使用 Lambda 上下文对象
](#python-handler-context)
+ [

## Python 处理程序的有效处理程序签名
](#python-handler-signature)
+ [

## 返回值
](#python-handler-return)
+ [

## 在处理程序中使用 适用于 Python (Boto3) 的 AWS SDK
](#python-handler-sdk)
+ [

## 评估环境变量
](#python-handler-env-vars)
+ [

## Python Lambda 函数的代码最佳实践
](#python-handler-best-practices)

## 示例 Python Lambda 函数代码
<a name="python-handler-example"></a>

以下示例 Python 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)会向该函数传递两个参数，一个是包含要处理的函数的数据的[事件对象](#python-handler-event)，另一个是包含函数调用相关信息的[上下文对象](#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 数据类型 | 
| --- | --- | 
| object | 字典（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 中定义处理程序函数时，该函数必须采用两个参数。这些参数中的第一个是 Lambda [事件对象](#python-handler-event)，第二个是 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 服务生成的事件和上下文对象使用特定的 AWS 类型，请将 `aws-lambda-typing` 程序包添加到函数的部署包中。您可以通过运行 `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]:
```

不能将 Python `async` 函数类型用于处理程序函数。

## 返回值
<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`（就像不具有 `return` 语句的 Python 函数隐式执行的那样），则运行时返回 `null`。
+ 如果您使用 `Event` 调用类型（一种[异步调用](invocation-async.md)），则该值将被丢弃。

在示例代码中，处理程序返回以下 Python 字典：

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

Lambda 运行时会序列化此字典，并将其作为 JSON 字符串返回到调用该函数的客户端。

**注意**  
在 Python 3.9 及更高版本中，Lambda 在错误响应中包含调用的 requestId。

## 在处理程序中使用 适用于 Python (Boto3) 的 AWS SDK
<a name="python-handler-sdk"></a>

通常，您将使用 Lambda 函数与其他 AWS 服务 和资源进行交互。与此类资源最简单的交互方法是使用 适用于 Python (Boto3) 的 AWS SDK。所有[支持的 Lambda Python 运行时](lambda-runtimes.md#runtimes-supported)都包含适用于 Python 的 SDK 的一个版本。但是，如果您的代码需要使用 SDK，强烈建议您将其包含在函数的部署包中。在部署包中包含 SDK 可以让您完全控制依赖项，并降低与其他库发生版本不一致问题的风险。要了解更多信息，请参阅 [Python 中的运行时系统依赖项](python-package.md#python-package-dependencies) 和 [向后兼容性](runtimes-update.md#runtime-update-compatibility)。

要在 Lambda 函数中使用适用于 Python 的 SDK，请在函数代码开头的导入块中添加以下语句：

```
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 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 参考](https://docs.aws.amazon.com/lambda/latest/api/welcome.html)以查看公开发布的 API 列表。

**编写幂等代码。**为您的函数编写幂等代码可确保以相同的方式处理重复事件。您的代码应该正确验证事件并优雅地处理重复事件。有关更多信息，请参阅[如何使我的 Lambda 函数具有幂等性？](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-idempotent/)。