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]),但最终拒绝在 stdlib 中包含用于注释生成器函数和高阶函数的特殊对象,因为这更适合第三方库;将它们包含在标准库中会引发太多棘手的问题。
  • 尽管对标准类型参数化语法进行了大量讨论,但已决定也将此留给第三方库。([7][8][9])。
  • 尽管进行了更多讨论,但已决定不标准化注解互操作性机制。此时标准化互操作性约定为时尚早。我们宁愿让这些约定根据实际使用和必要性自然发展,而不是试图强迫所有用户使用某种人为设计的方案。([13][14][15])。

参考文献和脚注


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

上次修改: 2023年9月9日 17:39:29 GMT