python的函数参数基础(一)(可能和别人讲的不一样,稍微深入一点)

  先让我说几句废话,函数(java里面叫方法)可能是我们了解python以后最基础的东西了(除了数据结构),写了无数个函数,你真的了解python的函数的设计逻辑以及思想吗?

 先上一小段我们最经常写的代码,我简单剖析一下:

1 def func_test(x,y,z):
2     print(x,y,z)
3     return None
4 
5 if __name__ == '__main__':
6  func_test(1,y=2,z=3)

上面这个代码很简单,就是定义了一个函数,让这个函数打印传入的三个参数,并且返回一个None,但是我在调用的时候的传参方式很别扭,第一个我没有写x=1,而是直接用的1,第二个参数我是用的y=2,但是第三个我也用的y=3,如果你改成下面这样:

1 def func_test(x,y,z):
2     print(x,y,z)
3     return None
4 
5 if __name__ == '__main__':
6  func_test(1,y=2,3)

那么编译器一定会报错,究其原因,是因为python的传参问题,因为python的函数接收参数列表方式有两种,第一种是*args(list),第二种是**kwargs(字典),某些角度上来说,第一个是一位数组,第二个是二维数组,但是如果你在一维数组中间,比如y的这个位置,使用了二位数组的传参方式y=2,那么第三个参数,也必须使用二位数组的传参方式,否则就会报错,但是,如果都不用y=这种方式,而用下面的方式,则不会报错:

1 def func_test(x,y,z):
2     print(x,y,z)
3     return None
4 
5 if __name__ == '__main__':
6  func_test(1,2,3)

剖析一下第二段代码,因为如果你在参数列表中间使用y=2,编译器会跳过这个参数,那么*args里面的参数列表就少了一个,原本*args里面应该是三个参数,如果跳过一个变成两个参数,那么这两个参数第一个肯定赋给x了,但是第二个应该赋给y还是赋给z,因为y=2被**kwargs捕获了,所以为了避免混淆,编译器第二个参数以及后面的参数必须被**kwargs捕获。

接下来在加一个东西,就是参数列表的类型,python是可以定义参数列表的类型的,但是由于是动态语言,所以一般情况下,python不会有严格的参数校验,但是有时候,我们还有相关的需求,所以这个时候就需要下面这种定义方式:

1 def func_test(x,y:str,z:int,i:list,j:dict,a:bool):
2     print(x,y,z,i,j,a)
3     return None
4 
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 def check(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 in enumerate(args):
 9             param = param_list[i]
10             if param.annotation is not param.empty and not isinstance(v, param.annotation):
11                 print(param.name,":",v, "!=", param_list[i].annotation)
12         for k, v in kwargs.items():
13             if param_map[k].annotation is not inspect._empty and not isinstance(v, param_map[k].annotation):
14                 print(k,':', v, '!=', param_map[k].annotation)
15         return fn(*args, **kwargs)
16     return wrapper
17 
18 @check
19 def func_test(x,y:str,z:int,i:list,j:dict,a:bool):
20     print(x,y,z,i,j,a)
21     return None
22 
23 if __name__ == '__main__':
24  func_test(1,2,3,[1,2],{2:3},a=0)

param的类是inspect.Parameter,具体参数可以自行百度,篇幅有限就不写太多了,ParamTer中有annotation这个变量,如果不指定paramter的类型,那么它就是inspect._empty类型,如果定义了类型,就是指定的类型,时间有限,这篇就讲到这里面了,下一篇写一下注解,因为我对注解也不是特别了解,需要深入了解以后才能写东西。

原文地址:https://www.cnblogs.com/dutu/p/10917217.html