先让我说几句废话,函数(java里面叫方法)可能是我们了解python以后最基础的东西了(除了数据结构),写了无数个函数,你真的了解python的函数的设计逻辑以及思想吗?
先上一小段我们最经常写的代码,我简单剖析一下:
1 deffunc_test(x,y,z):2 print(x,y,z)3 returnNone4
5 if __name__ == '__main__':6 func_test(1,y=2,z=3)
上面这个代码很简单,就是定义了一个函数,让这个函数打印传入的三个参数,并且返回一个None,但是我在调用的时候的传参方式很别扭,第一个我没有写x=1,而是直接用的1,第二个参数我是用的y=2,但是第三个我也用的y=3,如果你改成下面这样:
1 deffunc_test(x,y,z):2 print(x,y,z)3 returnNone4
5 if __name__ == '__main__':6 func_test(1,y=2,3)
那么编译器一定会报错,究其原因,是因为python的传参问题,因为python的函数接收参数列表方式有两种,第一种是*args(list),第二种是**kwargs(字典),某些角度上来说,第一个是一位数组,第二个是二维数组,但是如果你在一维数组中间,比如y的这个位置,使用了二位数组的传参方式y=2,那么第三个参数,也必须使用二位数组的传参方式,否则就会报错,但是,如果都不用y=这种方式,而用下面的方式,则不会报错:
1 deffunc_test(x,y,z):2 print(x,y,z)3 returnNone4
5 if __name__ == '__main__':6 func_test(1,2,3)
剖析一下第二段代码,因为如果你在参数列表中间使用y=2,编译器会跳过这个参数,那么*args里面的参数列表就少了一个,原本*args里面应该是三个参数,如果跳过一个变成两个参数,那么这两个参数第一个肯定赋给x了,但是第二个应该赋给y还是赋给z,因为y=2被**kwargs捕获了,所以为了避免混淆,编译器第二个参数以及后面的参数必须被**kwargs捕获。
接下来在加一个东西,就是参数列表的类型,python是可以定义参数列表的类型的,但是由于是动态语言,所以一般情况下,python不会有严格的参数校验,但是有时候,我们还有相关的需求,所以这个时候就需要下面这种定义方式:
1 deffunc_test(x,y:str,z:int,i:list,j:dict,a:bool):2 print(x,y,z,i,j,a)3 returnNone4
5 if __name__ == '__main__':6 func_test(1,'2',3,[1,2],{2:3},True)
看起来有点乱,大概的意思就是x不限定类型,y是str类型,z是int类型,是list类型,j是dict类型,a是bool类型,但是由于python不强制限制类型,最多会在pycharm里面提示你传的参数类型不对,编译器并不会强校验,这个时候,如果某些函数想要有这个功能就要加一个python的魔法小代码了,注解,我这段代码没有加强校验,只会打印,如果加,可以加一个assert:
1 defcheck(fn):2 @functools.wraps(fn)3 def wrapper(*args, **kwargs):4 sig = inspect.signature(fn) #截取函数签名
5 param_map = sig.parameters #获取参数列表的字典
6 param_list = list(param_map.values()) #获取参数列表
7 print(type(param_list[0]))8 for i, v inenumerate(args):9 param =param_list[i]10 if param.annotation is not param.empty and notisinstance(v, param.annotation):11 print(param.name,":",v, "!=", param_list[i].annotation)12 for k, v inkwargs.items():13 if param_map[k].annotation is not inspect._empty and notisinstance(v, param_map[k].annotation):14 print(k,':', v, '!=', param_map[k].annotation)15 return fn(*args, **kwargs)16 returnwrapper17
18 @check19 deffunc_test(x,y:str,z:int,i:list,j:dict,a:bool):20 print(x,y,z,i,j,a)21 returnNone22
23 if __name__ == '__main__':24 func_test(1,2,3,[1,2],{2:3},a=0)
param的类是inspect.Parameter,具体参数可以自行百度,篇幅有限就不写太多了,ParamTer中有annotation这个变量,如果不指定paramter的类型,那么它就是inspect._empty类型,如果定义了类型,就是指定的类型,时间有限,这篇就讲到这里面了,下一篇写一下注解,因为我对注解也不是特别了解,需要深入了解以后才能写东西。