Python函数注解
以下内容基于Python 3x
涉及的知识前提:
- 建议理解Python装饰器后学习此内容
函数注解概述
函数注解可以针对函数的参数、返回值添加元数据,其为注解。
python是动态语言,变量赋值时不会强制声明类型,且能随时重新赋值。无法像静态编译型语言一样,在编译时发现基本问题。
函数的参数要求,没有详细的doc string或更新没跟上,以至后续的使用者不能够清晰明白设计者要求的参数类型。以上行为导致的出错率变高,难以使用等问题。
函数注解可以对参数的要求类型进行注解,对函数返回值进行注解;其只是对做一个辅助性的说明,并不强制进行类型检查。
一些第三方工具(PyCharm)会对已定义的函数注解进行检查标记提示等,在编写代码时同样也可以通过编写一个装饰器,来进行一些检查。
实际应用
inspect模块
注解信息保存在函数的__annotations__属性中,函数的参数检查,一定是在函数实际运行之前,检查传递进来的形参与原有的注解类型是否匹配。
__annotations__属性是一个字典,对于位置参数的判断,较为复杂,可直接使用inspect模块获取函数签名信息。
# 注解x和y参数要求为int类型,返回结果为int类型
# Syntax:
def foo(x: int, y: int) -> int:
    return x + y
查看其__annotations__属性信息:
def foo(x: int, y: int) -> int:
    return x + y
print(type(foo.__annotations__))
print(foo.__annotations__)
# 输出结果:
<class 'dict'>
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
使用inspect模块获取签名信息:
inspect.signature返回的是一个OrderedDict(有序字典)
import inspect
def foo(x: int, y: int) -> int:
    return x + y
sig = inspect.signature(foo)
print(sig.parameters)
# 输出结果:
OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">)])
使用parameter方法,获取更详细的信息:
import inspect
def foo(x: int , y: int, z=20) -> int:
    return x + y + z
sig = inspect.signature(foo)
par = sig.parameters
print(par.items())
print(par["x"].name, "|-|", par["x"].annotation, "|-|", par["x"].default, "|-|", par["x"].empty, "|-|", par["x"].kind)
print(par["y"].name, "|-|", par["y"].annotation, "|-|", par["y"].default, "|-|", par["y"].empty, "|-|", par["y"].kind)
print(par["z"].name, "|-|", par["z"].annotation, "|-|", par["z"].default, "|-|", par["z"].empty, "|-|", par["z"].kind)
#输出结果:
odict_items([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('z', <Parameter "z=20">)])
x |-| <class 'int'> |-| <class 'inspect._empty'> |-| <class 'inspect._empty'> |-| POSITIONAL_OR_KEYWORD
y |-| <class 'int'> |-| <class 'inspect._empty'> |-| <class 'inspect._empty'> |-| POSITIONAL_OR_KEYWORD
z |-| <class 'inspect._empty'> |-| 20 |-| <class 'inspect._empty'> |-| POSITIONAL_OR_KEYWORD
parameter输出结果的信息保存在元组中,只读;
name:获取参数名字;
annotation:获取参数的注解,有可能没有定义,则为_empty;
default:返回参数的缺省值,如其中z参数的20即为缺省值;
empty:特殊的类,用来标记default属性或者注释annotation属性的空值;
kind:实参如何绑定到形参。以下是形参的类型:
- POSITIONAL_ONLY,值必须是位置参数提供的(Python中并没有绝对的位置参数,未实现)。 
- POSITIONAL_OR_KEYWORD,值可以作为关键字或者未知参数提供。 
- VAR_POSITIONAL,可变位置参数,对应*args。 
- KEYWORD_ONLY,keyword-only参数,对应*args之后出现的非可变关键字参数。 
- VAR_KEYWORD,可变关键字参数,对应**kwargs。 
业务代码
了解以上方法后,编写一个函数装饰器用来检测传参是否规范。
- 首先在函数运行之前,获取到函数签名、及签名的parameter信息将parameter信息。
- kv结构的value转换为列表保存。
- 使用for循环检查每个对应的parameter内参数(实际传的参数)注解是否不为空并且同时是否不是已知类型(isinstance函数可以检查传的值是否是已知类型,返回True或False,签名中参数的annotation类型为已知的)。
- 匹配通过,则说明传递的参数和注解要求类型一致,否则则抛出TypeError类型的自定义错误信息。
import inspect
import functools
def parameter_check(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        sig = inspect.signature(fn)
        parameter = sig.parameters
        value = list(parameter.values())
        for i, par in enumerate(args):
            if value[i].annotation is not value[i].empty and not isinstance(par, value[i].annotation):
                raise TypeError("Parameter type error")
        for k, v in kwargs.items():
            if parameter[k].annotation is not parameter[k].empty and not isinstance(v, parameter[k].annotation):
                raise TypeError("Parameter type error")
        ret = fn(*args, **kwargs)
        return ret
    return wrapper
@parameter_check
def foo(x: int, y: int, z=20) -> int:
    return x + y + z
print(foo(1, 50, 29))
# 输出结果:
80
# 更改传参,查看效果:
print(foo("hhh", 50, 29))
# 输出结果:
Traceback (most recent call last):
  File "E:/project/python/test.py", line 29, in <module>
    print(foo("hhh", 50, 29))
  File "E:/project/python/test.py", line 13, in wrapper
    raise TypeError("Parameter type error")
TypeError: Parameter type error
总结
通过对函数注解的了解,能够更加规范代码,提高效率,避免出错。在实际应用中亦可方便的展开使用。
参考链接:PEP 3107 — Function Annotations
Python函数注解的更多相关文章
- python 函数注解 参数后面加冒号 函数后面加箭头
		由于 python 是动态语言,在定义函数时,参数是不需要指定类型的.当调用别人写的函数,而该函数有没有文档说明,只有通过看源代码才能知道需要传递什么类型的参数. 不过 python 提供了一种机制可 ... 
- python 函数中的递归、lambda 、map reduce 等详解
		举例说明 #例1: ###递归函数求和 from traitlets.traitlets import Instance def mysum(L): print(L) if not L: return ... 
- Python函数参数和注解是什么
		四种参数 Python函数func定义如下: def func(first, *args, second="Hello World", **kwargs): print(first ... 
- python函数的参数细节
		按"指针"传递 python中变量赋值.参数传递都是通过"指针"拷贝的方式进行的.除了按"指针"拷贝,还有一种按值拷贝的方式,关于按值.按指 ... 
- Python3 函数注解
		Python3提供一种语法,用于为函数声明中的参数和返回值附加元数据.下面的例子是注解后的版本,特点在第一行: 1 def clip(text : str, max_len : 'int > 0 ... 
- [PY3]——函数——函数注解 | 实现类型检查功能
		函数注解(Function Annotations)——> 可以在定义函数的时候对参数和返回值添加注解 写函数注解 #平时我们使用help()可以查看一个函数的说明,我们自己写的函数也可以提供这 ... 
- 【290】Python 函数
		参考:Python 函数 参考:7.3 给函数参数增加元信息(增加参数的数据类型) 目录: 一.语法 二.说明 三.参数传递 四.参数 4. 1 必备参数 4.2 关键字参数 4.3 缺省参数 4.4 ... 
- python高级(五)—— python函数(一等对象)
		本文主要内容 一等对象 普通函数 & 高阶函数 可调用对象 & 自定义可调用类型 函数内省 函数注释 python高级——目录 文中代码均放在github上:https://githu ... 
- Python  函数常用方法总结
		一.函数的定义与优势: 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.函数能提高应用的模块性,和代码的重复利用率. Python提供了许多内建函数,比如print(),但也可以自己 ... 
随机推荐
- 数据结构-kmp算法
			定义 改进字符串的匹配算法 关键:通过实现一个包含了模式串的局部匹配信息的next()函数,利用匹配失败的信息,减少匹配次数. 1.BF算法 暴力匹配 给定 文本串S "BBC ABCDAB ... 
- 2019牛客暑期多校训练营(第八场)A.All-one Matrices(dp)
			题意:又是最大01矩阵的模型了 这次要找的是极大0/1矩阵的个数 思路:我们像处理最大01矩阵那样处理一下边界 由于我们上左右已经无法再继续扩展 我们只需要用前缀和记录一下是否可以向下扩展(即判断当前 ... 
- 【bzoj 2339】[HNOI2011]卡农(数论--排列组合+逆元+递推)
			题意:从编号为 1~N 的音阶中可选任意个数组成一个音乐片段,再集合组成音乐篇章.要求一个音乐篇章中的片段不可重复,都不为空,且出现的音符的次数都是偶数个.问组成 M 个片段的音乐篇章有多少种.答案取 ... 
- AtCoder Beginner Contest 183  E - Queen on Grid   (DP)
			题意:有一个\(n\)x\(m\)的棋盘,你需要从\((1,1)\)走到\((n,m)\),每次可以向右,右下,下走任意个单位,\(.\)表示可以走,#表示一堵墙,不能通过,问从\((1,1)\)走\ ... 
- Codeforces Round #650 (Div. 3)  D. Task On The Board  (构造,贪心)
			题意:有一个字符串和一组数,可以对字符串删去任意字符后为数组的长度,且可以随意排序,要求修改后的字符串的每个位置上的字符满足:其余大于它的字符的位置减去当前位置绝对值之和等于对应序列位置上的数. 题解 ... 
- Springboot 过滤器和拦截器详解及使用场景
			一.过滤器和拦截器的区别 1.过滤器和拦截器触发时机不一样,过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的.请求结束返回也是,是在servlet处理完后,返回给前端之前. 2.拦截 ... 
- 大数据开发-Spark Join原理详解
			数据分析中将两个数据集进行 Join 操作是很常见的场景.在 Spark 的物理计划阶段,Spark 的 Join Selection 类会根 据 Join hints 策略.Join 表的大小. J ... 
- Kubernets二进制安装(18)之安装部署Heapster
			1.下载heapster镜像 在运维主机(mfyxw50.mfyxw.com)执行 [root@mfyxw50 ~]# docker pull quay.io/bitnami/heapster:1.5 ... 
- 仿射加密与S-DES加密算法的实现
			仿射加密 #include <iostream> #include <cstdio> using namespace std; char letter[30]; char _l ... 
- vue template
			vue template <template> <div class="custom-class"> ... </div> </templ ... 
