Python中的函数注解
基本术语
PEP: PEP 代表Python增强提案。它是描述Python或其进程或环境的新特性的设计文档。它还向Python社区提供信息。
PEP 是提出主要新功能的主要机制,例如 - Python Web 服务器网关接口,收集社区对问题的输入并记录已在Python中实现的设计决策。
函数注释 - PEP 3107: PEP-3107 介绍了将任意元数据注释添加到Python的概念和语法。它是在 Python3 中引入的,之前是在Python 2.x 中使用外部库完成的
什么是函数注解?
函数注释是与函数的各个部分相关联的任意Python表达式。这些表达式在编译时评估,在 python 的运行时环境中没有生命。 Python对这些注释没有任何意义。当第三方库(例如 mypy)解释它们时,它们会夺走生命。
函数注释的目的:
函数注释的好处只能通过第三方库获得。福利的类型取决于图书馆的类型,例如
- Python支持动态类型,因此没有提供用于类型检查的模块。像这样的注解
[def foo(a:”int”, b:”float”=5.0) -> ”int”]
(语法在下一节中详细描述)可用于收集有关参数类型和函数函数发生的类型更改。 'mypy' 就是这样一个库。
- 库可以使用基于字符串的注释在编译时提供关于各种方法、类和模块的功能的更好的帮助消息。
函数注解的语法
它们就像参数名称后面的可选参数。
- 注意:下面提到的“表达式”一词可以是应该传递的参数类型或注释,也可以是外部库可以以有意义的方式使用的任意字符串。
- 简单参数的注释:参数名称后跟“:”,然后是表达式。注释语法如下所示。
def foobar(a: expression, b: expression = 5):
- 多余参数的注释:例如 *args 和 **kwargs 的多余参数,允许在函数调用中传递任意数量的参数。此类参数的注释语法如下所示。
def foobar(*args: expression, *kwargs: expression):
- 嵌套参数的注释:嵌套参数是Python 2x 的有用功能,其中元组在函数调用中传递并自动解包。此功能在Python 3x 中已删除,应手动解包。注释是在变量之后而不是在元组之后完成的,如下所示。
def foobar((a: expression, b: expression), (c: expression, d: expression)):
- 返回类型的注释:注释返回类型与注释函数参数略有不同。 '->' 后面是表达式,然后是 ':'。返回类型的注解语法如下所示。
def foobar(a: expression)->expression:
语法
decorator : ‘@’ name_ [‘(’ [arglist] ‘)’] NEWLINE
decorators : decorator+
funcdef : [decorators] ‘def’ NAME parameters [‘->’] ‘:’ suite
parameters : ‘(’ [typedarglist] ‘)’
typedarglist : (( tfpdef [‘=’ test] ‘, ’)* (‘*’ [tname]
(‘, ’ tname [‘=’ test])* [‘, ’ ‘ **’ tname] | ‘**’ tname)
| tfpdef [‘=’ test (‘, ’ tfpdef [‘=’ test])* [‘, ’]])
tname : NAME [‘:’ test]
tfpdef : tname | ‘(’ tfplist ‘)’
tfplist : tfpdef (‘, ’ tfpdef)* [‘, ’]
可视化语法:解析树由上述语法形成,以更好地可视化python函数和函数注释的语法。
示例代码
下面的代码将清除函数注释在运行时未评估的事实。该代码将斐波那契数列打印到“n”个位置。
# Python program to print Fibonacci series
def fib(n:'int', output:'list'=[])-> 'list':
if n == 0:
return output
else:
if len(output)< 2:
output.append(1)
fib(n-1, output)
else:
last = output[-1]
second_last = output[-2]
output.append(last + second_last)
fib(n-1, output)
return output
print(fib(5))
Output: [1, 1, 2, 3, 5]
注意:函数注解仅在Python 3x 中受支持。
访问函数注释
1. 使用 '__annotations__' :上面代码中的函数注解可以通过一个特殊的属性 '__annotations__' 来访问。它输出具有特殊键“return”的字典和具有注释参数名称的其他键。以下代码将打印注释。
# Python program to illustrate Function Annotations
def fib(n:'int', output:'list'=[])-> 'list':
if n == 0:
return output
else:
if len(output)< 2:
output.append(1)
fib(n-1, output)
else:
last = output[-1]
second_last = output[-2]
output.append(last + second_last)
fib(n-1, output)
return output
print(fib.__annotations__)
Output: {'return': 'list', 'n': 'int', 'output': 'list'}
2.使用标准模块'pydoc' :'pydoc'是一个标准的Python模块,它返回Python模块内的文档(如果有的话)。它有一个特殊的 'help()' 方法,它提供了一个交互式 shell 来获取任何关键字、方法、类或模块的帮助。 'help()' 可用于访问函数注释。下图显示了上述斐波那契数列代码中的函数注释。模块名称是“fib.py”。
3. 使用标准模块“inspect” :“inspect”模块提供了几个有用的函数来帮助获取有关活动对象的信息,例如模块、类、方法、函数、回溯、框架对象和代码对象。我们可以使用模块的“getfullargspec”方法来获取包含注释的函数的完整信息。
# Python program to illustrate Function Annotations
import inspect
def fib(n:'int', output:'list'=[])-> 'list':
if n == 0:
return output
else:
if len(output)< 2:
output.append(1)
fib(n-1, output)
else:
last = output[-1]
second_last = output[-2]
output.append(last + second_last)
fib(n-1, output)
return output
print(inspect.getfullargspec(fib))
Output: FullArgSpec(args=['n', 'output'], varargs=None,
varkw=None, defaults=([], ), kwonlyargs=[],
kwonlydefaults=None, annotations=
{'output': 'list', 'return': 'list', 'n': 'int'})
函数注解的应用
- 'mypy' 的使用: 'mypy' 是一个外部库,它借助函数注释提供静态类型检查。
为Python 2x 下载 mypyPython3xpip install mypy
pip install git+git://github.com/JukkaL/mypy.git
示例 1:# String slicing function that returns a string from start index to end index. def slice(string:str, start: int, end: int) -> str: return string[start:end] slice([1, 2, 3, 4, 5], 2, 4)
将上述代码另存为example.py,安装mypy后运行以下命令。确保您位于保存文件的目录中。
mypy example.py
您将得到以下结果。 - 当涉及到装饰器时,情况略有不同。
示例 2(a 部分):包装函数'gift_func' 和 'wrapped' 的参数的类型检查def wrapping_paper(func): def wrapped(gift:int): return 'I got a wrapped up {} for you'.format(str(func(gift))) return wrapped @wrapping_paper def gift_func(giftname:int): return giftname print(gift_func('gtx 5000'))
起初,似乎将字符串作为参数传递会返回错误,因为所需的数据类型是 'int',如 'gift_func' 和 'wrapped' 中注释的那样。 mypy 不会在包装函数参数中建立类型检查,但是可以检查装饰器的类型检查和包装函数的返回类型。因此,可以从上面的代码中得到以下结果。
- 示例 2(b 部分):对装饰器 'wrapping_paper' 的参数进行类型检查。
def wrapping_paper(func:str): def wrapped(gift:int): return 'I got a wrapped up {} for you'.format(str(func(gift))) return wrapped @wrapping_paper def gift_func(giftname:int): return giftname print(gift_func('gtx 5000'))
您现在将得到以下结果。
- 示例 2(c 部分):'gift_func' 和 'wrapped' 的返回类型的类型检查
# Suppose we want the return type to be int from typing import Callable def wrapping_paper(func): def wrapped(gift) -> int: return 'I got a wrapped up {} for you'.format(str(func(gift))) return wrapped @wrapping_paper def gift_func(giftname) -> int: return giftname print(gift_func('gtx 5000'))
您将得到以下结果。
- 示例 2(d 部分)包装函数“wrapping_paper”的返回类型的类型检查
# Suppose we want the return type to be int from typing import Callable def wrapping_paper(func) -> int: def wrapped(gift): return 'I got a wrapped up {} for you'.format(str(func(gift))) return wrapped @wrapping_paper def gift_func(giftname): return giftname print(gift_func('gtx 5000'))
你会得到以下结果