详解Python 函数参数的拆解
本文为阅读《PythonTricks:TheBook》一书的3.5FunctionArgumentUnpacking的笔记与扩充理解。函数参数拆解是定义可变参数(VarArgs)*args和**kwargs的反向特性。
*args和**kwars是函数可定义一个形参来接收传入的不定数量的实参。
而这里的函数参数拆解是形参定义多个,在调用时只传入一个集合类型对象(带上*或**前缀),如list,tuple,dict,甚至是generator,然后函数能自动从集合对象中取得对应的值。
如果能理解下面赋值时的参数拆解和Python3.5的新增***操作,那么于本文讲述的特性就好理解了。
唯一的不同时作为参数的集合传入函数时必须前面加上*或**,以此宣告该参数将被拆解,而非一个整体作为一个函数参数。加上*或**与Java的@SafeVarargs有类似的功效,最接近的是Scala的foo(Array[String]("d","e"):_*)写法。参见:Java和Scala调用变参的方式
Python的赋值拆解操作
>>>a,b=[1,2]#a,b=(1,2)也是一样的效果 >>>print(a,b) 12 >>>a,b={'x':1,'y':2} >>>print(a,b) xy >>>a,b={'x':1,'y':2}.keys() >>>print(a,b) xy >>>a,b={'x':1,'y':2}.values() >>>print(a,b) 12 >>>a,b=(x*xforxinrange(2)) >>>print(a,b) 01
Python3.5的新增拆解操作
>>>[1,2,*range(3),*[4,5],*(6,7)]#*号能把集合打散,flatten(unwrap) [1,2,0,1,2,4,5,6,7] >>>{'x':1,**{'y':2,'z':3}}#**把字典打散,flatten(unwrap)操作 {'x':1,'y':2,'z':3}
有些像是函数编程中的flatten或unwrap操作。
有了上面的基础后,再回到原书中的例子,当我们定义如下打印3-D坐标的函数
defprint_vector(x,y,z): print('<%s,%s,%s>'%(x,y,z))
依次传入三个参数的方式就不值不提了,现在就看如何利用函数的参数拆解特性,只传入一个集合参数,让该print_vector函数准确从集合中获得相应的x,y,和z的值。
函数参数拆解的调用举例
>>>list_vec=[2,1,3] >>>print_vector(*list_vec) <2,1,3> >>>print_vector(*(2,1,3)) <2,1,3> >>>dict_vec={'y':2,'z':1,'x':3} >>>print_vector(*dict_vec)#相当于print_vector(*dict_vec.keys())>>>print_vector(**dict_vec)#相当于print_vector(dict_vec['x'],dict_vec['y'],dict_vec['z'] <3,2,1> >>>genexpr=(x*xforxinrange(3)) >>>print_vector(*genexpr) <0,1,4> >>>print_vector(*dict_vec.values())#即print_vector(*list(dict_vec.values())) <2,1,3>
注意**dict_vec有点不一样,它的内容必须是函数print_vector的形参'x','y','z'作为key的三个元素。
以下是各种错误
**dict_vec元素个数不对,或key不匹配时的错误
>>>print_vector(**{'y':2,'z':1,'x':3}) <3,2,1> >>>print_vector(**{'y':2,'z':1,'a':3})#元素个数是3个,但出现x,y,z之外的key Traceback(mostrecentcalllast): File"",line1,in print_vector(**{'y':2,'z':1,'a':3}) TypeError:print_vector()gotanunexpectedkeywordargument'a' >>>print_vector(**{'y':2,'z':1,'x':3,'a':4})#包含有x,y,z,但有四个元素,key'a'不能识别 Traceback(mostrecentcalllast): File" ",line1,in print_vector(**{'y':2,'z':1,'x':3,'a':4}) TypeError:print_vector()gotanunexpectedkeywordargument'a' >>>print_vector(**{'y':2,'z':1})#缺少key'x'对应的元素 Traceback(mostrecentcalllast): File" ",line1,in print_vector(**{'y':2,'z':1}) TypeError:print_vector()missing1requiredpositionalargument:'x'
不带星星的错误
>>>print_vector([2,1,3]) Traceback(mostrecentcalllast): File"",line1,in print_vector([2,1,3]) TypeError:print_vector()missing2requiredpositionalarguments:'y'and'z'
把集合对象整体作为第一个参数,所以未传入y和z,因此必须用前缀*或**通告函数进行参数拆解
集合长度与函数参数个数不匹配时的错误
>>>print_vector(*[2,1])#拆成了x=2,y=1,然后z呢? Traceback(mostrecentcalllast): File"",line1,in print_vector(*[2,1]) TypeError:print_vector()missing1requiredpositionalargument:'z' >>>print_vector(*[2,1,3,4])#虽然拆出了x=2,y=1,z=3,但也别想强塞第四个元素给该函数(只定义的三个参数) Traceback(mostrecentcalllast): File" ",line1,in print_vector(*[2,1,3,4]) TypeError:print_vector()takes3positionalargumentsbut4weregiven
上面这两个错误与赋值时的拆解因元素个数不匹配时的错误是相对应的
>>>a,b=[1] Traceback(mostrecentcalllast): File"",line1,in a,b=[1] ValueError:notenoughvaluestounpack(expected2,got1) >>>a,b=[1,2,3] Traceback(mostrecentcalllast): File" ",line1,in a,b=[1,2,3] ValueError:toomanyvaluestounpack(expected2)
当然在赋值时Python可以像下面那样做
a,b,*c=[1,2,3,4] >>>print(a,b,c) 12[3,4]
补充(2020-07-02):迭代的拆解在Python中的术语是IterableUnpacking,找到两个相关的PEP448,PEP3132。在实际上用处还是很大的,比如在拆分字符串时只关系自己有兴趣的字段
line='2020-06-1922:14:002688abc.json' date,time,size,name=line.split()#获得所有字段值 _,time,_,name=line.split()#只对time和name有兴趣 date,*_=line.split()#只对第一个date有兴趣 *_,name=line.split()#只对最后的name有兴趣 date,*_,name=line.split()#对两边的date,name有兴趣
这样就避免了用索引号来引用拆分后的值,如split[0],splint[2]等,有名的变量不容易出错。注意到Python在拆解时非常聪明,它知道怎么去对应位置,用了星号(*)的情况,明白如何处理前面跳过多少个,中间跳过多少个,或最后收集多少个元素。
链接:
PEP448--AdditionalUnpackingGeneralizations
PEP3132--ExtendedIterableUnpacking
以上就是详解Python函数参数的拆解的详细内容,更多关于python函数参数拆解的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。