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 函数注解的具体细节之前,让我们首先广泛地讨论一下注解是什么以及不是什么。
- 函数注解,无论是参数还是返回值,都是完全可选的。
- 函数注解只不过是在编译时将任意 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: ...
但是,第一个示例中的字符串和第二个示例中的类型信息本身没有任何意义;意义仅来自第三方库。
- 根据第 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] 不进行此更改,因为
- 这将是不兼容的更改。
- Lambda 本身就被阉割了。
- 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__
是一个空的、可变的字典。
用例
在讨论注解的过程中,提出了一些用例。其中一些在此处列出,按它们传达的信息类型进行分组。还包括了可能利用注解的现有产品和包的示例。
标准库
pydoc 和 inspect
当显示函数的帮助信息时,pydoc
模块应该显示函数注解。inspect
模块应该更改为支持注解。
与其他 PEP 的关系
函数签名对象 (PEP 362)
函数签名对象应该公开函数的注解。Parameter
对象可能会更改,或者可能需要其他更改。
实现
参考实现已作为修订版 53170 [10] 检查到 py3k(以前称为“p3yk”)分支中。
被拒绝的提案
参考文献和脚注
版权
本文档已进入公有领域。
来源: https://github.com/python/peps/blob/main/peps/pep-3107.rst
上次修改: 2023年9月9日 17:39:29 GMT