Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 3107 – 函数注解

作者:
Collin Winter <collinwinter at google.com>, Tony Lownds <tony at lownds.com>
状态:
最终版
类型:
标准跟踪
创建日期:
2006年12月2日
Python 版本:
3.0
发布历史:


目录

摘要

本 PEP 引入了一种为 Python 函数添加任意元数据注解的语法 [1]

基本原理

由于 Python 2.x 系列缺乏标准的方式来注解函数的参数和返回值,因此出现了各种工具和库来填补这一空白。有些利用了 PEP 318 中引入的装饰器,而另一些则解析函数的文档字符串,寻找其中的注解。

本 PEP 旨在提供一种单一的、标准的方式来指定这些信息,从而减少迄今为止存在的机制和语法上的广泛差异所造成的混淆。

函数注解基础

在深入讨论 Python 3.0 函数注解的细节之前,让我们先广泛地谈谈注解是什么和不是什么。

  1. 函数注解,包括参数和返回值的注解,都是完全可选的。
  2. 函数注解仅仅是一种在编译时将任意 Python 表达式与函数的各个部分关联起来的方式。

    Python 本身不给注解附加任何特定的含义或意义。如访问函数注解所述,Python 只是简单地使这些表达式可用。

    注解获得意义的唯一方式是当它们被第三方库解释时。这些注解消费者可以对函数的注解做任何他们想做的事情。例如,一个库可能会使用基于字符串的注解来提供改进的帮助消息,就像这样:

    def compile(source: "something compilable",
                filename: "where the compilable thing comes from",
                mode: "is this a single statement or a suite?"):
        ...
    

    另一个库可能用于为 Python 函数和方法提供类型检查。这个库可以使用注解来指示函数的预期输入和返回类型,可能类似于:

    def haul(item: Haulable, *vargs: PackAnimal) -> Distance:
        ...
    

    然而,第一个例子中的字符串和第二个例子中的类型信息本身没有任何意义;意义仅来自第三方库。

  3. 根据第 2 点,本 PEP 不试图引入任何标准语义,即使是内置类型也不例外。这项工作将留给第三方库。

语法

参数

参数的注解采用可选表达式的形式,跟在参数名之后:

def foo(a: expression, b: expression = 5):
    ...

在伪语法中,参数现在看起来像 identifier [: expression] [= expression]。也就是说,注解总是出现在参数的默认值之前,并且注解和默认值都是可选的。就像等号用于指示默认值一样,冒号用于标记注解。所有注解表达式在函数定义执行时求值,就像默认值一样。

多余参数(即 *args**kwargs)的注解类似地表示:

def foo(*args: expression, **kwargs: expression):
    ...

嵌套参数的注解总是跟在参数名称之后,而不是最后一个括号。注解嵌套参数的所有参数并非必需:

def foo((x1, y1: expression),
        (x2: expression, y2: expression)=(None, None)):
    ...

返回值

到目前为止的例子都省略了如何注解函数返回值的例子。这可以通过以下方式完成:

def sum() -> expression:
    ...

也就是说,参数列表现在可以跟一个字面量 -> 和一个 Python 表达式。与参数的注解一样,这个表达式将在函数定义执行时求值。

函数定义的语法 [11] 现在是:

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
funcdef: [decorators] 'def' NAME parameters ['->' test] ':' suite
parameters: '(' [typedargslist] ')'
typedargslist: ((tfpdef ['=' test] ',')*
                ('*' [tname] (',' tname ['=' test])* [',' '**' tname]
                 | '**' tname)
                | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
tname: NAME [':' test]
tfpdef: tname | '(' tfplist ')'
tfplist: tfpdef (',' tfpdef)* [',']

Lambda

lambda 的语法不支持注解。lambda 的语法可以通过要求参数列表周围有括号来更改以支持注解。但是,决定 [12] 不进行此更改,因为:

  1. 这将是一个不兼容的更改。
  2. Lambda 函数反正也是残缺的。
  3. Lambda 函数总是可以更改为普通函数。

访问函数注解

编译后,函数的注解可通过函数的 __annotations__ 属性获取。此属性是一个可变字典,将参数名称映射到表示求值后的注解表达式的对象:

__annotations__ 映射中有一个特殊键,"return"。只有在为函数的返回值提供了注解时,此键才存在。

例如,以下注解:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ...

将导致 __annotations__ 映射为:

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

选择 return 键是因为它不会与参数名称冲突;任何尝试使用 return 作为参数名称都会导致 SyntaxError

如果函数上没有注解,或者函数是由 lambda 表达式创建的,那么 __annotations__ 将是一个空的、可变字典。

用例

在讨论注解的过程中,提出了许多用例。其中一些在此处呈现,按其传达的信息类型分组。还包括了现有产品和包的示例,这些产品和包可以利用注解。

  • 提供类型信息
    • 类型检查 ([3], [4])
    • 让 IDE 显示函数期望和返回的类型 ([16])
    • 函数重载 / 泛型函数 ([21])
    • 外语桥接 ([17], [18])
    • 适配 ([20], [19])
    • 谓词逻辑函数
    • 数据库查询映射
    • RPC 参数封送 ([22])
  • 其他信息
    • 参数和返回值的文档 ([23])

标准库

pydoc 和 inspect

pydoc 模块在显示函数的帮助信息时应显示函数注解。inspect 模块应更改以支持注解。

与其他PEP的关系

函数签名对象 (PEP 362)

函数签名对象应公开函数的注解。Parameter 对象可能会更改或需要其他更改。

实施

参考实现已作为修订版本 53170 [10] 检入到 py3k(以前为“p3yk”)分支。

被拒绝的提案

  • BDFL 拒绝了作者为生成器添加注解的特殊语法,认为它“太丑陋”了 [2]
  • 尽管早期讨论过([5], [6]),但在标准库中包含用于注解生成器函数和高阶函数的特殊对象最终被拒绝,认为它们更适合第三方库;将它们包含在标准库中引发了太多棘手的问题。
  • 尽管对标准类型参数化语法进行了大量讨论,但最终决定也将其留给第三方库。([7], [8], [9])。
  • 尽管进行了更多讨论,但最终决定不标准化注解互操作机制。此时标准化互操作约定还为时过早。我们宁愿让这些约定根据实际使用和需要自然发展,而不是试图强迫所有用户采用某种人为的设计方案。([13], [14], [15])。

参考文献和脚注


来源: https://github.com/python/peps/blob/main/peps/pep-3107.rst

最后修改: 2025-02-01 08:59:27 GMT