您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

Azure Functions Python 开发人员指南Azure Functions Python developer guide

本文介绍了如何使用 Python 开发 Azure Functions。This article is an introduction to developing Azure Functions using Python. 以下内容假定你已阅读 Azure Functions 开发人员指南The content below assumes that you've already read the Azure Functions developers guide.

有关 Python 中的独立函数示例项目,请参阅Python 函数示例For standalone Function sample projects in Python, see the Python Functions samples.

编程模型Programming model

Azure Functions 要求函数在 Python 脚本中作为无状态方法来处理输入并生成输出。Azure Functions expects a function to be a stateless method in your Python script that processes input and produces output. 默认情况下,运行时期望此方法在 __init__.py 文件中作为名为 main() 的全局方法实现。By default, the runtime expects the method to be implemented as a global method called main() in the __init__.py file. 还可以指定备用入口点You can also specify an alternate entry point.

来自触发器和绑定的数据使用在 function.json 配置文件中定义的 name 属性,通过方法特性绑定到函数。Data from triggers and bindings is bound to the function via method attributes using the name property defined in the function.json file. 例如,下面的 function.json 描述一个由名为 req 的 HTTP 请求触发的简单函数:For example, the function.json below describes a simple function triggered by an HTTP request named req:

{
  "bindings": [
    {
      "name": "req",
      "direction": "in",
      "type": "httpTrigger",
      "authLevel": "anonymous"
    },
    {
      "name": "$return",
      "direction": "out",
      "type": "http"
    }
  ]
}

__init__.py 文件包含以下函数代码:The __init__.py file contains the following function code:

def main(req):
    user = req.params.get('user')
    return f'Hello, {user}!'

你还可以使用 Python 类型注释在函数中显式声明属性类型和返回类型。you can also explicitly declare the attribute types and return type in the function using Python type annotations. 这有助于你使用由许多 Python 代码编辑器提供的 intellisense 和自动完成功能。This helps you use the intellisense and autocomplete features provided by many Python code editors.

import azure.functions


def main(req: azure.functions.HttpRequest) -> str:
    user = req.params.get('user')
    return f'Hello, {user}!'

使用 azure.functions.* 包中附带的 Python 注释将输入和输出绑定到方法。Use the Python annotations included in the azure.functions.* package to bind input and outputs to your methods.

备用入口点Alternate entry point

您可以选择在scriptFile 函数 json文件中指定和entryPoint属性,从而更改函数的默认行为。You can change the default behavior of a function by optionally specifying the scriptFile and entryPoint properties in the function.json file. 例如,下面的 function.json 指示运行时使用 main.py 文件中的 customentry() 方法作为 Azure 函数的入口点。For example, the function.json below tells the runtime to use the customentry() method in the main.py file, as the entry point for your Azure Function.

{
  "scriptFile": "main.py",
  "entryPoint": "customentry",
  "bindings": [
      ...
  ]
}

文件夹结构Folder structure

Python 函数项目的文件夹结构类似于以下示例:The folder structure for a Python Functions project looks like the following example:

 FunctionApp
 | - MyFirstFunction
 | | - __init__.py
 | | - function.json
 | | - example.py
 | - MySecondFunction
 | | - __init__.py
 | | - function.json
 | - SharedCode
 | | - myFirstHelperFunction.py
 | | - mySecondHelperFunction.py
 | - host.json
 | - requirements.txt

存在共享的 host.json 文件,可用于配置函数应用。There's a shared host.json file that can be used to configure the function app. 每个函数都有自己的代码文件和绑定配置文件 (function.json)。Each function has its own code file and binding configuration file (function.json).

共享代码应保留在单独的文件夹中。Shared code should be kept in a separate folder. 若要引用 SharedCode 文件夹中的模块,可以使用以下语法:To reference modules in the SharedCode folder, you can use the following syntax:

from __app__.SharedCode import myFirstHelperFunction

若要引用函数的本地模块,可以使用相对导入语法,如下所示:To reference modules local to a function, you can use the relative import syntax as follows:

from . import example

在 Azure 中将 Functions 项目部署到函数应用时,FunctionApp 文件夹的整个内容应包含在包中,但不包含该文件夹本身。When deploying a Function project to your function app in Azure, the entire content of the FunctionApp folder should be included in the package, but not the folder itself.

触发器和输入Triggers and Inputs

在 Azure Functions 中,输入分为两种类别:触发器输入和附加输入。Inputs are divided into two categories in Azure Functions: trigger input and additional input. 虽然它们在 function.json 文件中并不相同,但它们在 Python 代码中的使用方法却是相同的。Although they are different in the function.json file, usage is identical in Python code. 在本地运行时,触发器和输入源的连接字符串或机密应映射到 local.settings.json 文件中的值,以及在 Azure 中运行时的应用程序设置。Connection strings or secrets for trigger and input sources map to values in the local.settings.json file when running locally, and the application settings when running in Azure.

例如,以下代码演示了这两个类别之间的差异:For example, the following code demonstrates the difference between the two:

// function.json
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "req",
      "direction": "in",
      "type": "httpTrigger",
      "authLevel": "anonymous",
      "route": "items/{id}"
    },
    {
      "name": "obj",
      "direction": "in",
      "type": "blob",
      "path": "samples/{id}",
      "connection": "AzureWebJobsStorage"
    }
  ]
}
// local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "AzureWebJobsStorage": "<azure-storage-connection-string>"
  }
}
# __init__.py
import azure.functions as func
import logging


def main(req: func.HttpRequest,
         obj: func.InputStream):

    logging.info(f'Python HTTP triggered function processed: {obj.read()}')

调用函数时,HTTP 请求作为 req 传递给函数。When the function is invoked, the HTTP request is passed to the function as req. 将基于路由 URL 中的 ID 从 Azure Blob 存储检索一个条目,并在函数体中将其用作 objAn entry will be retrieved from the Azure Blob Storage based on the ID in the route URL and made available as obj in the function body. 在这里,指定的存储帐户是在 AzureWebJobsStorage 中找到的连接字符串,它与函数应用使用的存储帐户相同。Here the storage account specified is the connection string found in AzureWebJobsStorage which is the same storage account used by the function app.

outputsOutputs

输出可以在返回值和输出参数中进行表示。Output can be expressed both in return value and output parameters. 如果只有一个输出,则建议使用返回值。If there's only one output, we recommend using the return value. 对于多个输出,必须使用输出参数。For multiple outputs, you'll have to use output parameters.

若要使用函数的返回值作为输出绑定的值,则绑定的 name 属性应在 function.json 中设置为 $returnTo use the return value of a function as the value of an output binding, the name property of the binding should be set to $return in function.json.

若要生成多个输出,请使用 azure.functions.Out 接口提供的 set() 方法将值分配给绑定。To produce multiple outputs, use the set() method provided by the azure.functions.Out interface to assign a value to the binding. 例如,以下函数可以将消息推送到队列,还可返回 HTTP 响应。For example, the following function can push a message to a queue and also return an HTTP response.

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "req",
      "direction": "in",
      "type": "httpTrigger",
      "authLevel": "anonymous"
    },
    {
      "name": "msg",
      "direction": "out",
      "type": "queue",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    },
    {
      "name": "$return",
      "direction": "out",
      "type": "http"
    }
  ]
}
import azure.functions as func


def main(req: func.HttpRequest,
         msg: func.Out[func.QueueMessage]) -> str:

    message = req.params.get('body')
    msg.set(message)
    return message

日志记录Logging

可在函数应用中通过根 logging 处理程序来访问 Azure Functions 运行时记录器。Access to the Azure Functions runtime logger is available via a root logging handler in your function app. 此记录器绑定到 Application Insights,并允许标记在函数执行期间遇到的警告和错误。This logger is tied to Application Insights and allows you to flag warnings and errors encountered during the function execution.

下面的示例在通过 HTTP 触发器调用函数时记录信息消息。The following example logs an info message when the function is invoked via an HTTP trigger.

import logging


def main(req):
    logging.info('Python HTTP trigger function processed a request.')

有其他日志记录方法可用于在不同跟踪级别向控制台进行写入:Additional logging methods are available that let you write to the console at different trace levels:

方法Method 描述Description
critical(_message_) 在根记录器中写入具有 CRITICAL 级别的消息。Writes a message with level CRITICAL on the root logger.
error(_message_) 在根记录器中写入具有 ERROR 级别的消息。Writes a message with level ERROR on the root logger.
warning(_message_) 在根记录器中写入具有 WARNING 级别的消息。Writes a message with level WARNING on the root logger.
info(_message_) 在根记录器中写入具有 INFO 级别的消息。Writes a message with level INFO on the root logger.
debug(_message_) 在根记录器中写入具有 DEBUG 级别的消息。Writes a message with level DEBUG on the root logger.

若要了解有关日志记录的详细信息,请参阅Monitor Azure FunctionsTo learn more about logging, see Monitor Azure Functions.

HTTP 触发器和绑定HTTP Trigger and bindings

HTTP 触发器在函数 jon 文件中定义。The HTTP trigger is defined in the function.jon file. name绑定的必须与函数中的命名参数匹配。The name of the binding must match the named parameter in the function. 在前面的示例中,使用了req一个绑定名称。In the previous examples, a binding name req is used. 此参数是一个HttpRequest对象,并返回一个httpresponse.cache对象。This parameter is an HttpRequest object, and an HttpResponse object is returned.

通过HttpRequest对象,可以获取请求标头、查询参数、路由参数和消息正文。From the HttpRequest object, you can get request headers, query parameters, route parameters, and the message body.

下面的示例来自 Python 的HTTP 触发器模板The following example is from the HTTP trigger template for Python.

def main(req: func.HttpRequest) -> func.HttpResponse:
    headers = {"my-http-header": "some-value"}

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')
            
    if name:
        return func.HttpResponse(f"Hello {name}!", headers=headers)
    else:
        return func.HttpResponse(
             "Please pass a name on the query string or in the request body",
             headers=headers, status_code=400
        )

在此函数中, name查询参数的值是paramsHttpRequest对象的参数获取的。In this function, the value of the name query parameter is obtained from the params parameter of the HttpRequest object. 使用get_json方法读取 JSON 编码的消息正文。The JSON-encoded message body is read using the get_json method.

同样,你可以在返回status_codeheaders httpresponse.cache对象中为响应消息设置和。Likewise, you can set the status_code and headers for the response message in the returned HttpResponse object.

异步Async

建议使用 async def 语句将 Azure 函数编写为异步协同例程。We recommend that you write your Azure Function as an asynchronous coroutine using the async def statement.

# Will be run with asyncio directly


async def main():
    await some_nonblocking_socket_io_op()

如果 main ()函数是同步的(没有限定符),我们会在asyncio线程池中自动运行该函数。If the main() function is synchronous (no qualifier), we automatically run the function in an asyncio thread-pool.

# Would be run in an asyncio thread-pool


def main():
    some_blocking_socket_io()

上下文Context

若要在执行过程中获取函数的调用上下文,请在其签名中包含 context 参数。To get the invocation context of a function during execution, include the context argument in its signature.

例如:For example:

import azure.functions


def main(req: azure.functions.HttpRequest,
         context: azure.functions.Context) -> str:
    return f'{context.invocation_id}'

Context 类具有以下方法:The Context class has the following methods:

function_directory
在其中运行函数的目录。The directory in which the function is running.

function_name
函数的名称。Name of the function.

invocation_id
当前函数调用的 ID。ID of the current function invocation.

全局变量Global variables

不保证应用的状态可保留到将来的执行。It is not guaranteed that the state of your app will be preserved for future executions. 不过,Azure Functions 运行时通常会重复使用同一个进程来多次执行同一个应用。However, the Azure Functions runtime often reuses the same process for multiple executions of the same app. 为了缓存高开销计算的结果,请将其声明为全局变量。In order to cache the results of an expensive computation, declare it as a global variable.

CACHED_DATA = None


def main(req):
    global CACHED_DATA
    if CACHED_DATA is None:
        CACHED_DATA = load_json()

    # ... use CACHED_DATA in code

环境变量Environment variables

在函数中,应用程序设置(如服务连接字符串)在执行过程中公开为环境变量。In Functions, application settings, such as service connection strings, are exposed as environment variables during execution. 可以通过声明import ossetting = os.environ["setting-name"]使用来访问这些设置。You can access these settings by declaring import os and then using, setting = os.environ["setting-name"].

下面的示例获取应用程序设置,其中包含名为myAppSetting的键:The following example gets the application setting, with the key named myAppSetting:

import logging
import os
import azure.functions as func

def main(req: func.HttpRequest) -> func.HttpResponse:

    # Get the setting named 'myAppSetting'
    my_app_setting_value = os.environ["myAppSetting"]
    logging.info(f'My app setting value:{my_app_setting_value}')

对于本地开发,应用程序设置将保留在本地的设置文件中For local development, application settings are maintained in the local.settings.json file.

Python 版本和包管理Python version and package management

当前,Azure Functions 仅支持 Python 3.6.x(CPython 正式分发版)。Currently, Azure Functions only supports Python 3.6.x (official CPython distribution).

在使用 Azure Functions Core Tools 或 Visual Studio Code 进行本地开发时,将所需包的名称和版本添加到 requirements.txt 文件并使用 pip 安装它们。When developing locally using the Azure Functions Core Tools or Visual Studio Code, add the names and versions of the required packages to the requirements.txt file and install them using pip.

例如,可以使用以下要求文件和 pip 命令从 PyPI 安装 requests 包。For example, the following requirements file and pip command can be used to install the requests package from PyPI.

requests==2.19.1
pip install -r requirements.txt

发布到 AzurePublishing to Azure

准备好进行发布时,请确保所有依赖项都已在 requirements.txt 文件(位于项目目录的根目录)中列出。When you're ready to publish, make sure that all your dependencies are listed in the requirements.txt file, which is located at the root of your project directory. Azure Functions 可以远程生成这些依赖项。Azure Functions can remotely build these dependencies.

从发布中排除的项目文件和文件夹(包括虚拟环境文件夹)将在 funcignore 文件中列出。Project files and folders that are excluded from publishing, including the virtual environment folder, are listed in the .funcignore file.

若要部署到 Azure 并执行远程生成,请使用以下命令:To deploy to Azure and perform a remote build, use the following command:

func azure functionapp publish <app name> --build remote

如果使用的不是远程生成,并且使用需要编译器的包,并且不支持从 PyPI 安装许多 Linux 兼容的轮,则在不进行本地生成的情况下发布到 Azure 将会失败,并出现以下错误:If you're not using remote build, and using a package that requires a compiler and does not support the installation of many Linux-compatible wheels from PyPI, publishing to Azure without building locally will fail with the following error:

There was an error restoring dependencies.ERROR: cannot install <package name - version> dependency: binary dependencies without wheels are not supported.  
The terminal process terminated with exit code: 1

若要在本地生成并配置所需的二进制文件,请在本地计算机上安装 Docker ,并运行以下命令以使用Azure Functions Core Tools (func)进行发布。To build locally and configure the required binaries, install Docker on your local machine and run the following command to publish using the Azure Functions Core Tools (func). 请记住将 <app name> 替换为 Azure 中的函数应用名称。Remember to replace <app name> with the name of your function app in Azure.

func azure functionapp publish <app name> --build-native-deps

实际上,Core Tools 会使用 docker 将 mcr.microsoft.com/azure-functions/python 映像作为本地计算机上的容器来运行。Underneath the covers, Core Tools will use docker to run the mcr.microsoft.com/azure-functions/python image as a container on your local machine. 在此环境中,它随后会构建并安装源分发中所需的模块,然后将它们打包,以便最终部署到 Azure。Using this environment, it will then build and install the required modules from source distribution, before packaging them up for final deployment to Azure.

若要构建依赖项并使用持续交付(CD)系统发布,请使用 Azure PipelinesTo build your dependencies and publish using a continuous delivery (CD) system, use Azure Pipelines.

单元测试Unit Testing

可以使用标准测试框架,像测试其他 Python 代码一样测试以 Python 编写的函数。Functions written in Python can be tested like other Python code using standard testing frameworks. 对于大多数绑定,可以通过创建 azure.functions 包中相应类的实例来创建模拟输入对象。For most bindings, it's possible to create a mock input object by creating an instance of an appropriate class from the azure.functions package. 由于不能直接使用 azure.functions 包,请务必根据前面的 Python 版本和包管理部分中所述,通过 requirements.txt 文件安装该包。Since the azure.functions package is not immediately available, be sure to install it via your requirements.txt file as described in Python version and package management section above.

例如,下面是 HTTP 触发的函数的模拟测试:For example, following is a mock test of an HTTP triggered function:

{
  "scriptFile": "httpfunc.py",
  "entryPoint": "my_function",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}
# myapp/httpfunc.py
import azure.functions as func
import logging

def my_function(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        return func.HttpResponse(f"Hello {name}")
    else:
        return func.HttpResponse(
             "Please pass a name on the query string or in the request body",
             status_code=400
        )
# myapp/test_httpfunc.py
import unittest

import azure.functions as func
from httpfunc import my_function


class TestFunction(unittest.TestCase):
    def test_my_function(self):
        # Construct a mock HTTP request.
        req = func.HttpRequest(
            method='GET',
            body=None,
            url='/api/HttpTrigger',
            params={'name': 'Test'})

        # Call the function.
        resp = my_function(req)

        # Check the output.
        self.assertEqual(
            resp.get_body(),
            b'Hello Test',
        )

下面是使用队列触发的函数的另一个示例:Here is another example, with a queue triggered function:

# myapp/__init__.py
import azure.functions as func


def my_function(msg: func.QueueMessage) -> str:
    return f'msg body: {msg.get_body().decode()}'
# myapp/test_func.py
import unittest

import azure.functions as func
from . import my_function


class TestFunction(unittest.TestCase):
    def test_my_function(self):
        # Construct a mock Queue message.
        req = func.QueueMessage(
            body=b'test')

        # Call the function.
        resp = my_function(req)

        # Check the output.
        self.assertEqual(
            resp,
            'msg body: test',
        )

已知问题和常见问题解答Known issues and FAQ

所有已知问题和功能请求都使用 GitHub 问题列表进行跟踪。All known issues and feature requests are tracked using GitHub issues list. 如果遇到 GitHub 中未列出的问题,请打开“新问题”并提供问题的详细说明。If you run into a problem and can't find the issue in GitHub, open a new issue and include a detailed description of the problem.

跨域资源共享Cross-origin resource sharing

Azure Functions 支持跨域资源共享(CORS)。Azure Functions supports cross-origin resource sharing (CORS). CORS 是在门户中和通过Azure CLI配置的。CORS is configured in the portal and through the Azure CLI. CORS 允许的源列表应用于 function app 级别。The CORS allowed origins list applies at the function app level. 启用 CORS 后,响应包括Access-Control-Allow-Origin标头。With CORS enabled, responses include the Access-Control-Allow-Origin header. 有关详细信息,请参阅 跨域资源共享For more information, see Cross-origin resource sharing.

Python function apps目前不支持允许的源列表。The allowed origins list isn't currently supported for Python function apps. 由于此限制,你必须在 HTTP 函数中Access-Control-Allow-Origin明确设置标头,如以下示例中所示:Because of this limitation, you must expressly set the Access-Control-Allow-Origin header in your HTTP functions, as shown in the following example:

def main(req: func.HttpRequest) -> func.HttpResponse:

    # Define the allow origin headers.
    headers = {"Access-Control-Allow-Origin": "https://contoso.com"}

    # Set the headers in the response.
    return func.HttpResponse(
            f"Allowed origin '{headers}'.",
            headers=headers, status_code=200
    )

确保还更新了函数 json,以支持 OPTIONS HTTP 方法:Make sure that you also update your function.json to support the OPTIONS HTTP method:

    ...
      "methods": [
        "get",
        "post",
        "options"
      ]
    ...

Chrome 浏览器使用此方法来协商允许的来源列表。This method is used by the Chrome browser to negotiate the allowed origins list.

后续步骤Next steps

有关详细信息,请参阅以下资源:For more information, see the following resources: