
Python:类型注解、inspect
发布日期:2021-05-07 14:08:02
浏览次数:17
分类:原创文章
本文共 6459 字,大约阅读时间需要 21 分钟。
目录
函数定义的弊端
Python是动态语言,变量随时可以被赋值,且能赋值为不同的类型
Python不是静态编译型语言,变量类型是在运行器决定的
动态语言很灵活,但是这种特性也是弊端
def add(x,y): return x + yprint(add(1,2))print(add("Hello","Word"))print(add(1,"Word")) #Python是强类型这里会报错
- 难发现:由于不做任何类型检查,直到运行期问题才显现出来,或者线上运行时才能暴露出问 题
- 难使用:函数的使用者看到函数的时候,并不知道你的函数的设计,并不知道应该传入什么类 型的数据
如何解决这种动态语言定义的弊端尼
1、 增加文档Documentation String
- 函数提供说明文档【但是函数定义更新了,文档未必同步更新】
def add(x,y): ''' :param int:x: :param int:y: :return int: ''' return x + yprint(add(1,2))print(add("Hello","Word"))print(add(1,"Word"))
2、函数注解
- Python 3.5引入的
- 对函数的参数进行类型注解
- 对函数的返回值进行类型注解
- 只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
- 提供给第三方工具,做代码分析,发现隐藏的bug
- 函数注解的信息,保存在__annotations__属性
3、变量注解
- Python 3.6引入【i : int = 3】
业务应用
函数参数类型检查
- 函数参数的检查,一定是在函数外
- 函数应该作为参数,传入到检查函数中
- 检查函数拿到函数传入的实际参数,与形参声明对比
- __annotations__属性是一个字典,其中包括返回值类型的声明。假设要做位置参数的判断,无 法和字典中的声明对应。使用inspect模块
inspet模块
- 提供获取对象信息的函数,可以检查函数和类、类型检查
inspect模块
Parameter对象
- 保存在元组中,是只读的
- name,参数的名字
- annotation,参数的注解,可能没有定义
- default,参数的缺省值,可能没有定义
- empty,特殊的类,用来标记default属性或者注释annotation属性的空值
- kind,实参如何绑定到形参,就是形参的类型
- POSITIONAL_ONLY,值必须是位置参数提供
- POSITIONAL_OR_KEYWORD,值可以作为关键字或者位置参数提
- VAR_POSITIONAL,可变位置参数,对应*args
- KEYWORD_ONLY,keyword-only参数,对应*或者*args之后的出现的非可变关键字参数
- VAR_KEYWORD,可变关键字参数,对应**kwargs
import inspectdef add(x, y:int=7, *args, z, t=10,**kwargs) -> int: return x + ysig = inspect.signature(add)print(sig)print('params : ', sig.parameters) # 有序字典 params : OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y: int = 7">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)])print('return : ', sig.return_annotation) #return : <class 'int'>print('~~~~~~~~~~~~~~~~')for i, (name ,param) in enumerate(sig.parameters.items()): print(i+1, name, param.annotation, param.kind, param.default) print(param.default is param.empty, end='\n\n')结果:(x, y: int = 7, *args, z, t=10, **kwargs) -> intparams : OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y: int = 7">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)])return : <class 'int'>~~~~~~~~~~~~~~~~1 x <class 'inspect._empty'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>True2 y <class 'int'> POSITIONAL_OR_KEYWORD 7False3 args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'>True4 z <class 'inspect._empty'> KEYWORD_ONLY <class 'inspect._empty'>True5 t <class 'inspect._empty'> KEYWORD_ONLY 10False6 kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'>True
signature(callable),获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它 所在的类和名称空间及其他信息
import inspectdef add(x:int, y:int, *args,**kwargs) -> int: return x + ysig = inspect.signature(add)print(sig, type(sig)) # 函数签名 #(x: int, y: int, *args, **kwargs) -> int <class 'inspect.Signature'>print('params : ', sig.parameters) # OrderedDict params : OrderedDict([('x', <Parameter "x: int">), ('y', <Parameter "y: int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])print('return : ', sig.return_annotation) #return : <class 'int'>print(sig.parameters['y'], type(sig.parameters['y'])) #y: int <class 'inspect.Parameter'>print(sig.parameters['x'].annotation) # <class 'int'>print(sig.parameters['args']) # *argsprint(sig.parameters['args'].annotation) #<class 'inspect._empty'>print(sig.parameters['kwargs']) # **kwargsprint(sig.parameters['kwargs'].annotation) #<class 'inspect._empty'>
inspect模块is
返回的True或False
print(inspect.isfunction(add)) #是否是函数print(inspect.ismethod(add)) #判断是否类的方法print( inspect.isgenerator(add)) #是否是生成器对象print(inspect.isgeneratorfunction(add)) #是否是类print(inspect.ismodule(inspect)) #是否是模块print(inspect.isbuiltin(print)) #是否是内建对象
业务中适合使用
如:请检查用户输入是否符合参数注解的要求
- 调用时,判断用户输入的实参是否符合要求
- 调用时,用户感觉还是在调用add函数
- 对用户输入的数据和声明的类型进行对比,如果不符合,提示用户
def add(x:int,y:int=7) -> int: return x + y #print(add(7))#print(add(4,y=8))#print(add(x=4,y=8))#print(add(4,y=8))#print(add("end","is"))
判断代码
import inspectimport functoolsdef check(fn): @functools.wraps(fn) def wrapper(*args,**kwargs): print(args,kwargs) #接受到传入的参数 sig = inspect.signature(fn) print(sig) print('params : ', sig.parameters) # 有序字典 print('return : ', sig.return_annotation) params = sig.parameters #位置参数 params_list = list(params.keys()) tmp_list = [0]*len(params_list) for i ,v in enumerate(args): k = params_list[i] tmp_list[i] = 1 if isinstance(v, params[k].annotation) : print(v, "is", params[k].annotation) else: errstr = "{} {} {}".format(v, "not is", params[k].annotation) raise TypeError(errstr) #关键字处理 if len(kwargs) !=0: for k,v in kwargs.items(): if isinstance(v,params[k].annotation): print(v,"is",params[k].annotation) else: errstr = "{} {} {}".format(v, "not is", params[k].annotation) raise TypeError(errstr) else: for k,v in enumerate(tmp_list): if tmp_list[k] == 0: print("{}".format(params_list[k]),"is default ",params[params_list[k]].default) ret = fn(*args,**kwargs) return ret return wrapper@checkdef add(x:int, y:int=7) -> int: return x + y#print(add(7))#print(add(4,y=8))#print(add(x=4,y=8))#print(add(4,y=8))#print(add("end","is")) #不可调用,检查失败
对业务稍加变形
def add(x,y:int=7) -> int: return x + y
import inspectdef check(fn): def wrapper(*args, **kwargs): sig = inspect.signature(fn) params = sig.parameters values = list(params.values()) for i,p in enumerate(args): param = values[i] if param.annotation is not param.empty and not isinstance(p, param.annotation): print(p,'!==',values[i].annotation) break for k,v in kwargs.items(): if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation): print(k,v,'!===',params[k].annotation) break return fn(*args, **kwargs) return wrapper@checkdef add(x, y:int=7) -> int: return x + y#print(add(10,20))#print(add(x=10,y=20))#print(add(10,y=20))print(add("end","class"))
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2025年03月23日 04时38分12秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
刷题计划1——poj1753
2019-03-04
蓝桥杯备战——刷题(2019)
2019-03-04
未定义的变量“py”或函数“py.command”
2019-03-04
我们,都一样......(句句入心)
2019-03-04
总结了一下c/c++函数和变量的命名规则
2019-03-04
关于构造函数内调用虚函数的问题
2019-03-04
最短路径问题—Dijkstra算法
2019-03-04
求二叉树的深度
2019-03-04
万物皆可爬系列查看翻页翻到最后是什么
2019-03-04
A Guide to Node.js Logging
2019-03-04
webwxbatchgetcontact一个神奇的接口
2019-03-04
Edge浏览器:你的的内核我的芯
2019-03-04
git命令升级版用法
2019-03-04
sed常用命令
2019-03-04
checksec未完待续~
2019-03-04
python pexpect
2019-03-04
inode索引节点的概念
2019-03-04
create-react-app第一步
2019-03-04
怎么去利用已有的数据做分析?
2019-03-04
某易游戏经典吃豆豆动画404页面源码
2019-03-04