Python高级 —— 弥补动态类型的短板(函数注解、变量注解、inspect)
Python 函数注解
函数注解
用途:弥补python的动态语言存在的弊端,即某变量无法为其直接声明类型,在代码编写及测试阶段没有出现异常,但是当项目上线后由于数据类型导致程序出错。 如编写一个求和的函数,但是没有在函数内对输入数据的类型进行完全的判断,导致用户输入数值与字符串从而报错的情况
注意:该功能不提供强制性的限制功能,只提供一个辅助说明功能。常用于提供给第三方工具,做代码分析,发现隐藏Bug
适用: python 3.5以后
实现形式:该功能将注解存储在函数的 .__annotations__属性中
错误的情况:
更好的做法:
变量注解
形式: i:int = 5
注意:该功能不提供强制性的限制功能,只提供一个辅助说明功能。常用于提供给第三方工具,做代码分析,发现隐藏Bug
适用: python 3.6以后
实现形式:该功能将注解存储在函数的 .__annotations__属性中,但此属性对应的数据类型为字典(普通的无序字典)
应用场景
常用于业务中对函数及其参数类型进行检测,以弥补 Python 动态类型带来的一些弊端
借助 inspect 模块
功能:用于检测函数的函数名、参数、缺省值、函数定义等相关信息,对非函数的可调用对象均有效
主要方法:
import inspect
inspect. signature(func)
如:
查询函数签名
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print('sig is : ', sig)
print('sig type is : ', type(sig))
查询函数参数的有序字典
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print('\n')
print(sig.parameters)
print(type(sig.parameters))
print('\n')
parameter对象
parameters是一个有序的字典,parameter是字典中每个参数的类型,可通过parameter对象获取如下信息:
属性 | 含义 |
---|---|
parameterObj.name | 参数名 |
parameterObj.annotation | 参数注解 |
parameterObj.default | 参数缺省值 |
parameterObj.kind | 形参类型 |
parameterObj.empty | 该参数无函数注解的class |
parameterObj.empty 是非常重要的一个属性,会返回一个type。后续要用到这个属性来判断某个参数是否设置了注解,从而判断传入的实参与形参是否对应。若缺失了该属性,则难以完成该功能。
print(para.empty)
print(type(para.empty))
查询函数返回值
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print('\n')
print(sig.return_annotation)
print('\n')
print(type(sig.return_annotation))
print('\n')
查询函数某个参数及其返回值类型
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print('\n')
print(sig.parameters['x'])
print('\n')
print(type(sig.parameters['x']))
print('\n')
查询函数某个参数的返回值类型
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print('\n')
print(sig.parameters['y'].annotation)
print('\n')
print(type(sig.parameters['y'].annotation))
print('\n')
收集参数无可查询的类型
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
return x + y
sig = inspect.signature(add)
print('\n')
print(sig.parameters['args'])
print('\n')
print(sig.parameters['args'].annotation)
print('\n')
inspect.isxxx 判断对象类型
函数 | 说明 |
---|---|
ispect.isfunction(add) | 是否为函数 |
inspect.ismethod(add) | 是否为类的方法 |
inspect.isgenerator(add) | 是否为生成器对象 |
inspect.isgeneratorfunction(add) | 是否为生成器函数 |
inspect.isclass(add) | 是否为类 |
inspect.ismodule(add) | 是否为模块 |
inspect.isbuiltin(add) | 是否为内建模块 |
业务场景
检测函数传入的实参是否符合形参的注解要求
import inspect
import functools
class ParameterTypeNotAvailable(Exception):
pass
def check(func):
@functools.wraps(func)
def _wrapper(*args, **kwargs):
# get the signature from the the function --- func
sig = inspect.signature(func)
# get parameters' object ordered dictionary
params = sig.parameters
# check whether the parameters putted into the func is same
# as the sign of function's annotation
# depend on the order
paraObjList = tuple(params.values())
for i, eachPara in enumerate(args):
# make sure the parameter annotation is entered
if not (paraObjList[i].annotation == paraObjList[i].empty):
if not isinstance(eachPara, paraObjList[i].annotation):
raise ParameterTypeNotAvailable
# kwargs
for kwKey, kwValue in kwargs.items():
# make sure the parameter annotation is entered
if not (params[kwKey].annotation == params[kwKey].empty):
if not isinstance(kwValue, params[kwKey].annotation):
raise ParameterTypeNotAvailable
res = func(*args, **kwargs)
return res
return _wrapper
@check
def add(x: int, y) -> int:
return x + y
print(add(1, 2))
add()函数注解了x为int类型,y未定义具体类型
当传入的x、y均为整数的时候,程序正常运行,结果如下
但当传入的x变为1.0的时候,程序会抛出异常,结果如下:
其他用法
获取源码
借助 inspect.getsource(func)
如:print(inspect.getsource(logit_test))
通过命令行调用该功能
python -m inspect logging
python -m inspect requests:Request
获取 doc
借助 inspect.getdoc(func)
inspect.getdoc(logit_test)
获取文件所在的位置
借助 inspect.getfile(func)
获取生成器当前状态
import inspect
def get(n):
for i in range(n):
yield i
gen = get(2)
print(inspect.getgeneratorstate(gen))
print(next(gen))
print(inspect.getgeneratorstate(gen))
for i in gen:
print(i)
print(inspect.getgeneratorstate(gen))
获取生成器当前返回的值
inspect.getgeneratorlocals
import inspect
def get(n):
for i in range(n):
yield i
gen = get(2)
print(inspect.getgeneratorlocals(gen))
next(gen)
print(inspect.getgeneratorlocals(gen))
next(gen)
print(inspect.getgeneratorlocals(gen))