📜  Python中的函数注解

📅  最后修改于: 2022-05-13 01:55:46.761000             🧑  作者: Mango

Python中的函数注解

基本术语

PEP: PEP 代表Python增强提案。它是描述Python或其进程或环境的新特性的设计文档。它还向Python社区提供信息。
PEP 是提出主要新功能的主要机制,例如 - Python Web 服务器网关接口,收集社区对问题的输入并记录已在Python中实现的设计决策。

函数注释 - PEP 3107: PEP-3107 介绍了将任意元数据注释添加到Python的概念和语法。它是在 Python3 中引入的,之前是在Python 2.x 中使用外部库完成的

什么是函数注解?

函数注释是与函数的各个部分相关联的任意Python表达式。这些表达式在编译时评估,在 python 的运行时环境中没有生命。 Python对这些注释没有任何意义。当第三方库(例如 mypy)解释它们时,它们会夺走生命。

函数注释的目的:
函数注释的好处只能通过第三方库获得。福利的类型取决于图书馆的类型,例如

  1. Python支持动态类型,因此没有提供用于类型检查的模块。像这样的注解
    [def foo(a:”int”, b:”float”=5.0)  -> ”int”]

    (语法在下一节中详细描述)可用于收集有关参数类型和函数函数发生的类型更改。 'mypy' 就是这样一个库。

  2. 库可以使用基于字符串的注释在编译时提供关于各种方法、类和模块的功能的更好的帮助消息。

函数注解的语法

它们就像参数名称后面的可选参数。

    注意:下面提到的“表达式”一词可以是应该传递的参数类型或注释,也可以是外部库可以以有意义的方式使用的任意字符串。
  1. 简单参数的注释:参数名称后跟“:”,然后是表达式。注释语法如下所示。
    def foobar(a: expression, b: expression = 5):
    
  2. 多余参数的注释:例如 *args 和 **kwargs 的多余参数,允许在函数调用中传递任意数量的参数。此类参数的注释语法如下所示。
    def foobar(*args: expression, *kwargs: expression):
    
  3. 嵌套参数的注释:嵌套参数是Python 2x 的有用功能,其中元组在函数调用中传递并自动解包。此功能在Python 3x 中已删除,应手动解包。注释是在变量之后而不是在元组之后完成的,如下所示。
    def foobar((a: expression, b: expression), (c: expression, d: expression)):
    
  4. 返回类型的注释注释返回类型与注释函数参数略有不同。 '->' 后面是表达式,然后是 ':'。返回类型的注解语法如下所示。
    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 下载 mypy
    pip install mypy

    Python3x
    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'))
    

    你会得到以下结果