14 - 函数参数检测-inspect模块
1 python类型注解
类型注解,即对变量的类型,进行标注或者说明,因为Python是一门动态编译型语言,我们无法在赋值时就定义它的变量类型,所以在Python3.5
以上版本新增了类型注解,但仅仅是提示作用,并不能严格控制,这是动态编译型语言的通病,下面来仔细看一下什么是Python的类型注解。
2 函数定义的弊端
Python是动态语言,变量随时可以被赋值,且赋值为不同的类型,这就与静态语言不同了,变量的类型是在运行期决定的,而静态语言事先就已经定义好了变量的类型了。这是动态语言方便之处,但也是一种弊端,我们无法控制变量的类型,也就无法控制异常的产生。举个栗子
def add(x,y):
return x + y
print(add(1,2))
print(add('s','b'))
print(add(1,'a'))
当用户传入两个数字时,返回它们的和,但是如果我们传递其他变量呢?比如字符串,因为Python中实现了+号的类型重载,所以说两个字符串的确可以加,但是如果是数字和字符串呢?在Python这种强类型语言中来说,属于非法操作(javascript会隐式转换),而这时,我们就需要对用户传入的数据进行类型判断,不符合本函数的需求,那么就抛个异常,或者提示等等操作,这样就不会引起后续代码在执行期崩溃。如何解决呢?其实主要有两种方式。
- 函数文档
- 函数注解
3 函数文档
在函数中插入说明性文档的方式成为函数文档。
def add(x, y):
"""
This function used to add something
:param x: int object
:param y: int object
:return: int object
"""
return x + y
在函数中,一般是定义语句后的首行使用三对双引号表示。通常存储在函数的__doc__属性中。当用户使用help(函数)时,会被打印在屏幕上。
In [68]: def add(x, y):
...: """
...: This function used to add something
...: :param x: int object
...: :param y: int object
...: :return: int object
...: """
...: return x + y
...:
In [69]: help(add)
Help on function add in module __main__:
add(x, y)
This function used to add something
:param x: int object
:param y: int object
:return: int object
In [70]: print(add.__doc__)
This function used to add something
:param x: int object
:param y: int object
:return: int object
In [71]:
每次都要使用help来查看函数的说明,的确可以让使用者了解函数的参数以及返回值的类型,但并不是所有人都愿意写doc的,在这个所谓的敏捷开发时代,人们大多会以敏捷开发为借口没时间写,所以这种方法不是很用。
4 函数注解
Python的函数注解是什么呢?首先来看一下如下代码:
def add(x: int, y: int) -> int:
return x + y
- 函数的位置形参,和默认值形参后使用冒号分隔,后面用于标识变量期望的类型。
- 在def语句末尾,使用->符号后 指定用于标识函数执行后的返回值类型。
完成以上定义后,,主要的差别如下图:
当我们在IDE中准备传入非注释类型变量时,IDE会帮我们进行颜色提示,用于表示这里传入的变量有点问题。在编写时我们尚且可以使用这种方式,对我们产生一点'警示',但是当我们写的函数被其他人调用的时候,那么就无法进行'提示'了,这个时候,我们就需要对传入的参数进行类型检查了。
我们来总结一下:
- 函数注解在Python3.5中引入
- 对函数的参数、返回值进行类型注解
- 只对函数的参数做一个辅助的说明,并不对函数参数进行类型检查
- 提供给第三方工具,做代码分析,发现隐藏的BUG
- 函数注解的信息,保存在函数的
__annotation__
属性中。
python3.6
以上还添加了变量的注解:i:int = 10
,当然也只是提示的作用。
4.1 annotation属性
在Python中使用__开头的表示符一般被用特殊属性,__annotation__
存储的就是函数的签名信息
In [71]: def add(x: int, y: int) -> int:
...: return x + y
...:
In [73]: add.__annotations__
Out[73]: {'x': int, 'y': int, 'return': int}
当我们使用变量注释时,变量名和类型就会存放在函数的__annotations__属性中。那么即然有变量存储,那么我们是不是只需要获取传入的参数,然后和annotations中存储的变量类型进行比较是不是就达到目的了呢?仔细思考一下:
- 参数检查势必要在函数执行前,想要在add执行前添加参数判断那么就需要使用装饰器了
- __annotations__的值是一个字典,字典是无序的,用户按照位置传进来参数是有序的,如何让它们形成对应关系方便我们检测呢?
下面我们来了解一下inspect模块,它可以帮我们完成这个事情。
5 inspect模块
官方解释如下:inspect模块提供了几个有用的函数来帮助获取关于活动对象的信息,例如模块、类、方法、函数、回溯、框架对象和代码对象。例如,它可以帮助您检查类的内容、检索方法的源代码、提取并格式化函数的参数列表,或者获取显示详细回溯所需的所有信息。
5.1 常用方法
分类 | 方法名称 | 功能 |
---|---|---|
判断 | inspect.getmodulename(path) | 获取模块名称 |
inspect.ismodule(object) | 是不是个模块 | |
inspect.isclass(object) | 是不是个类 | |
inspect.ismethod(object) | 是不是一个方法 | |
inspect.isfunction(object) | 是不是一个函数 | |
inspect.isgeneratorfunction(object) | 是不是一个生成器函数 | |
inspect.isgenerator(object) | 是不是一个生成器 | |
inspect.iscoroutinefunction(object) | 是不是一个协程函数 | |
获取信息 | inspect.getmodulename(path) | 获取模块名称 |
inspect.getsource(object) | 获取对象的原码(并不会解析装饰器原码) |
5.2 signature类
首先我们要说的是函数的签名信息:它包含了了函数的函数名、它的参数类型,它所在的类和名称空间及其他信息,签名对象(signature object)表示可调用对象的调用签名信息和它的注解信息,当我们使用signature()时,它会重新返回一个包含可调用对象信息的签名对象。
5.3 parameters属性
signature类的parameters
属性,它里面存放的是函数的参数注解和返回值注解,组成的有序字典,其中参数注解的格式为:参数名称,使用inspect.Parameters类包装的参数注解,这个参数注解很强大,它包含如下常用的方法:
方法名称 | 含义 |
---|---|
empty | 等同于inspect._empty表示一个参数没有被类型注释 |
name | 参数的名称 |
default | 参数的默认值,如果一个参数没有默认值,这个属性的值为inspect.empty |
annotation | 参数的注解类型,如果参数没有定义注解,这个属性的值为inspect.empty |
kind | 参数的类型 |
这里的参数类型表示的是inspect内置参数类型(其实就是几个常用的函数参数定义类型而已,只是换个名字而已)
_POSITIONAL_ONLY = _ParameterKind.POSITIONAL_ONLY # 位置参数_only
_POSITIONAL_OR_KEYWORD = _ParameterKind.POSITIONAL_OR_KEYWORD # 位置或关键字参数
_VAR_POSITIONAL = _ParameterKind.VAR_POSITIONAL # 可变位置参数
_KEYWORD_ONLY = _ParameterKind.KEYWORD_ONLY # keyword-only参数
_VAR_KEYWORD = _ParameterKind.VAR_KEYWORD # 可变关键字参数
其中POSITIONAL_ONLY,Python中没有被实现。
5.4 获取对象的参数签名
根据上面讲的方法,我们可以通过如下方式,简单的获取参数的签名:
In [11]: import inspect
...:
...: def add(x: int, y: int) -> int:
...: return x + y
...:
...: sig = inspect.signature(add)
...: params = sig.parameters
...: print(params)
OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">)])
In [21]: params['x'].annotation
Out[21]: int # 如果没有定义x的参数注解,那么这里就是inspect._empty
通过它的属性,搭配有序字典
这个特性,有没有很兴奋?参数有序,传入的实参有序,还能获取参数注解的类型,那么就可以开工进行参数检查了!
6 检查参数
以上面函数为例子,当给add函数传入的x,y时进行参数检查,如果x,y不是int类型,那么返回异常,并退出函数
import inspect
import functools
def check(fn):
@functools.wraps(fn) # 等于 wrapper.__annotation__ = fn.__annotation__ 还有其他的属性比如__doc__,__module__等
def wrapper(*args, **kwargs):
sig = inspect.signature(fn) # 获取add函数签名信息
params = sig.parameters # 获取add函数的参数信息
values = list(params.values()) # 由于params是个有序字典,那么values也是有序的,只需根据索一一对应判断即可
for i, k in enumerate(args): # 遍历用户传入的位置参数
if values[i].annotation != inspect._empty: # 如果定义了参数注解,则开始检查
if not isinstance(k, values[i].annotation): # 如果检查不通过,曝出异常
raise('Key Error')
for k,v in kwargs.items():
if params[k].annotation != inspect._empty:
if not isinstance(v,params[k].annotation):
raise('Key Error')
return fn(*args, **kwargs)
return wrapper
@check
def add(x: int, y: int) -> int:
return x + y
add(4,y=5)
14 - 函数参数检测-inspect模块的更多相关文章
- inspect模块详解
inspect模块主要提供了四种用处: (1).对是否是模块,框架,函数等进行类型检查. (2).获取源码 (3).获取类或函数的参数的信息 (4).解析堆栈 使用inspect模块可以提供自省功能, ...
- python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性
python inspect 模块 和 types 模块 判断是否是方法,模块,函数等内置特殊属性 inspect import inspect def fun(): pass inspect.ism ...
- 【集成学习】sklearn中xgboot模块中fit函数参数详解(fit model for train data)
参数解释,后续补上. # -*- coding: utf-8 -*- """ ############################################## ...
- python——inspect模块
inspect模块常用功能 import inspect # 导入inspect模块 inspect.isfunction(fn) # 检测fn是不是函数 inspect.isgenerator((x ...
- inspect模块的使用
一.介绍 inspect模块主要的四种用处: 1.对是否是模块.框架.函数等进行类型检测 2.获取源码 3.获取类或函数的参数信息 4.解析堆栈 二.使用 只写了2个自己用到的方法,方法太用,http ...
- Python常用函数、方法、模块记录
常用函数: 1.pow():乘方 2.abs():绝对值 3.round():四舍五入 4.int():转换为整数 5.input():键盘输入(会根据用户的输入来做类型的转换) raw_input( ...
- JavaScript函数参数与调用
函数调用: /* 1. 函数调用 */ ,,,); /* 2. 方法调用 */ this.CName = "全局"; var o = { CName:"o类", ...
- Python函数参数的五种类型
之前项目需求,需要通过反射获取函数的参数,python中可以通过函数签名(signature)来实现. 首先需要了解函数参数的类型,Python的参数类型一共有5种:POSITIONAL_OR_KEY ...
- Python基础(协程函数、内置函数、递归、模块和包)-day05
写在前面 上课第五天,打卡: 凭着爱,再回首: 一.协程函数(生成器:yield的表达式形式) 1.yield 的语句形式: yield 1 - 这种方式在 Python基础(函数部分)-day04 ...
随机推荐
- 51nod 1674 区间的价值V2(思维+拆位+尺取法)
最近被四区题暴虐... 题意:lyk拥有一个区间. 它规定一个区间的价值为这个区间中所有数and起来的值与这个区间所有数or起来的值的乘积. 例如3个数2,3,6.它们and起来的值为2,or起来的值 ...
- 解析php addslashes()与addclashes()函数的区别和比较
一. addslashes() 函数 addslashes(string) 函数在指定的预定义字符前添加反斜杠.这些预定义字符是:•单引号 (')•双引号 (")•反斜杠 (\)•NULL ...
- BZOJ5011 JXOI2017颜色(主席树)
相当于求满足在子段中出现的颜色只在该子段中出现的非空子段数量.这也就相当于其中出现的颜色最左出现的位置在左端点右侧,最右出现的位置在右端点左侧.那么若固定某个端点,仅考虑对该端点的限制,会有一段合法区 ...
- 获取http和ftp地址的图片
根据http和ftp图片地址获取对应图片的缩略图和原图 public class GetBitmapImageClass { public BitmapSource GetImageHttp(stri ...
- [您有新的未分配科技点]博弈论入门:被博弈论支配的恐惧(Nim游戏,SG函数)
今天初步学习了一下博弈论……感觉真的是好精妙啊……希望这篇博客可以帮助到和我一样刚学习博弈论的同学们. 博弈论,又被称为对策论,被用于考虑游戏中个体的预测行为和实际行为,并研究他们的应用策略.(其实这 ...
- IOS8模糊毛玻璃的效果UIVisualEffectView
UIVisualEffectView实现两种模糊效果:UIBlurEffect 和 UIVibrancyEffect 两者都是继承自UIView,前者放在任意的View里边都能对下册的视图渲染出模糊效 ...
- 【ListBox】ListBox的相关操作
Winform中两个listbox的操作是平时比较常用的操作. 本次将以一个Winform实例来分享一下两个listbox的操作,包括:listbox添加项,项的上移下移等操作. 假设有两个listb ...
- 常州day7
Task1 蛤布斯有一个序列,初始为空.它依次将 1-n 插入序列,其中 i 插到当前第 ai 个数的右边 (ai=0 表示插到序列最左边).它希望你帮 它求出最终序列. 对于 100%的数据,n&l ...
- 【BZOJ4651】【NOI2016】网格(Tarjan,哈希)
[BZOJ4651][NOI2016]网格(Tarjan,哈希) 题面 BZOJ 洛谷 题解 首先把题目稍微变得好说一些,给定一个网格,已经删去了若干个格子 问最少删去多少个格子使得图不连通. 这题的 ...
- Unity3D手游开发日记(3) - 场景加载进度条的完美方案
我以为做个进度条很简单,分分钟解决,结果折腾了一天才搞定,Unity有很多坑,要做完美需要逐一解决. 问题1:最简单的方法不能实现100%的进度 用最简单的方法来实现,不能实现100%的进度,原因是U ...