一、函数注解
1、什么是函数注解
这是在python 3.5才引进的新功能,可以给函数的参数和返回值做一个注解,但这个并不是强制类型的,也就说,就算加了函数注解,也只是一个辅助说明,并不会对函数参数进行类型检查,只能提供给函数调用者一些友好的提示信息。
函数注解的信息,保存在特殊属性__annotations__中
2、如何使用函数注解
在定义函数的时候,参数注解是在每个参数后面使用‘:’说明参数类型,返回值注解是在参数列表后面使用‘->’指定返回值类型。
注意:如果参数有默认值,默认值应该放在参数注解后面
格式:def add(x:int , y:str='a') -> int :
#定义一个添加了函数注解的f1函数
def f1(x:int,y:str,z:dict={1:3}) -> int:
pass
#通过特殊属性获取函数注解
>>>f1.__annotations__
{'x': int, 'y': str, 'z': dict, 'return': int}
3、函数注解的应用
虽然函数注解不具备强制性,不做类型检查,但是我们自己可以通过拿到函数注解,自己写一个检查函数来做类型检查。
检查函数的分析如下:
① 检查功能不应该写在每个函数中,应该抽离出来,单独写一个具有检查功能的检查函数
② 被检查的函数应该作为参数,传入到检查函数中
③ 检查函数要能拿到传入被检查函数的实际参数,然后和__annotations__里面的类型做判断
这么一分析,好像我们前面学的装饰器非常适合做这件事。
#具有检查功能的装饰器函数
def check(fn):
def wrapper(*args,**kwargs):
for k1,v1 in fn.__annotations__.items():
for k2,v2 in kwargs.items():
if k1 == k2 and not isinstance(v2,v1):
print('error')
return
result = fn(*args,**kwargs)
return result
return wrapper
#只需要在被检查函数前面加个装饰器
@check
def f1(x:int,y:str,z:dict={1:3}) -> int:
pass
#传参类型不满足函数注解的类型,所以报错了
>>>f1(x=3,y=2,z={1:2})
error
二、Inspect模块
问题分析:通过自己写了一个检查装饰器函数,好像可以解决问题,但是我们要找到,特殊属性__annotations__是一个字典,字典是无序的,如果我们传的是位置参数呢,好像就无法正确的进行匹对检查了。
问题解决:借助Inspect模块可以解决这个问题
步骤1:获取函数的签名
格式:Inspect.signature(callable) -> sig(函数签名)
import inspect
#定义一个演示函数
def add(x:int, y:int=3, *args,**kwargs) -> int:
return x + y
#获取函数的签名
>>>sig = inspect.signature(add)
步骤2:通过函数签名的两个属性,拿到参数列表字典和返回值类型
格式:sig.parameters -> 一个有序字典,key是每一个参数,对应的value是每个参数封装的一个parameter对象
格式:sig.return__annotation -> 返回值类型
#一个有序字典
>>>sig.parameters
OrderedDict([('x', ), ('y', ), ('args', ), ('kwargs', )])
#返回值类型
>>>sig.return_annotation
步骤3:通过parameter对象的属性,获取相应的值
格式:sig.parameters['参数名称'].属性
parameter对象有如下常用到属性
name:参数的名字
annotation:参数的注解,可能没有定义
default:参数的默认值,可能没有定义
empty:可以判断一个参数注解是否为空
kind:形参的类型(位置参数,可变位置参数,keyword_only参数,可变关键字参数),对应如下:
POSITIONAL_ONLY:位置参数
POSITIONAL_OR_KEYWORD:位置参数或关键字参数
VAR_POSITIONAL:可变位置参数,对应*args
KEYWORD_ONLY:keyword-only参数
VAR_KEYWORD:可变关键字参数,对应**kwargs
#通过parameters字典找到参数对应的parameter对象,再通过对象的属性获取响应值
>>>print(sig.parameters['x'].annotation)
>>>print(sig.parameters['x'].name)
>>>print(sig.parameters['x'].kind)
x
POSITIONAL_OR_KEYWORD
三、实际业务问题处理
通过inspect模块来写一个检查装饰器函数,可以检查调用函数传递的实参是否满足参数注解的要求。
比如:def add(x:int, y:int=3, *args:int,**kwargs) -> int:
#定义一个检查装饰器函数
def check(fn):
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
parlist = list(sig.parameters.values())
for i in parlist:
if str(i.kind) == 'VAR_POSITIONAL':
var_position = parlist.index(i)
if str(i.kind) == 'VAR_KEYWORD':
var_keyword = parlist.index(i)
for k,v in enumerate(args):
if k
if parlist[k].annotation is not parlist[k].empty:
if not isinstance(v,parlist[k].annotation):
print('args error')
return
else:
if parlist[var_position].annotation is not parlist[var_position].empty:
if not isinstance(v,parlist[var_position].annotation):
print('args error')
return
for k,v in kwargs.items():
if k in sig.parameters:
if sig.parameters[k].annotation is not sig.parameters[k].empty and not isinstance(v,sig.parameters[k].annotation):
print('kwargs error')
return
result = fn(*args,**kwargs)
return result
return wrapper
#在需要检查的函数使用装饰器
@check
def add(x:int, y:int=3, *args:int,**kwargs) -> int:
return x + y
#验证
>>>add(3,y=10,z='3')
13
>>>add(3,y='a',z='3')
kwargs error
>>>add(3,4,'a')
args error