本文将讨论Python的函数参数。我们将了解什么是args和kwargs,/and是。这个问题虽然是一个python的基础问题,但是我们在写代码的时候经常会遇到。比如timm中就广泛使用了这样一种参数传递方式。定义和传递参数parameters和arguments有什么区别?很多人互换使用这些术语,但有一个区别:Parameters是函数定义中定义的名称Arguments是传递给函数的值红色是参数,绿色是参数中传递参数的两种方式我们可以按位置传递参数和关键字。在下面的示例中,我们将值hello作为位置参数传递。valueworlddefthe_func(greeting,thing)passedwithkeywords:print(greeting+''+thing)the_func('hello',thing='world')位置参数和kwargs(关键字参数)的区别在于传递位置参数的顺序参数很重要。如果你调用the_func('world','hello')它会打印worldhello。kwargs传递的顺序并不重要:the_func('hello','world')#->'helloworld'the_func('world','hello')#->'worldhello'the_func(greeting='你好',thing='world')#->'helloworld'the_func(thing='world',greeting='hello')#->'helloworld'the_func('hello',thing='world')#->'helloworld'只要kwarg在位置参数之后,您就可以混合和匹配位置参数和关键字参数。以上就是我们在python教程中经常看到的内容。让我们继续函数参数。我们将演示函数参数传递的6种方法,这些方法可以涵盖所有问题。1.如何获取所有未捕获的位置参数使用*args让它接收不指定数量的形参。defmultiply(a,b,*args):result=a*bforarginargs:result=result*argreturnresult在这个函数中,我们通常定义前两个参数(a和b)。然后使用args将所有剩余参数打包到一个元组中。将其视为获取其他未处理的参数并将它们收集到一个名为“args”的元组变量中:multiply(1,2)#returns2multiply(1,2,3,4)#returns24最后一次调用将值1赋给参数a,2到参数b,并用(3,4)填充arg变量。由于这是一个元组,我们可以在函数中循环遍历它,并使用值相乘!2.如何获取所有未捕获的关键字参数类似于args,这次是两个星号*kwargsdefintroduce(firstname,lastname,**kwargs):introduction=f"Iam{firstname}{lastname}"forkey,valueinkwargs.items():introduction+=f"my{key}is{value}"returnintroduction**kwargs关键字将转换所有不匹配的关键字参数存储在一个名为kwargs的字典中。然后可以像上面的函数一样访问这个字典。print(introduce(firstname='mike',lastname='huls'))#返回“我是mikehuls”print(introduce(firstname='mike',lastname='huls',age=33,website='mikehuls.com'))#我是mikehuls我的年龄是33我的网站是overfit.cn3.如果你只想接受关键字参数,你怎么设计一个可以强制函数只接受关键字参数的函数。deftransfer_money(*,from_account:str,to_account:str,amount:int):print(f'Transfering${amount}FORM{from_account}to{to_account}')transfer_money(from_account='1234',to_account='6578',amount=9999)#不会工作:TypeError:transfer_money()接受0个位置参数,但是给了1个位置参数(和2个关键字参数)transfer_money('1234',to_account='6578',amount=9999)#won'twork:TypeError:transfer_money()takes0positionalargumentsbut3weregiventransfer_money('1234','6578',9999)并且没有变量接受它,也就是说,它被忽略了。4.如何设计一个只接受位置参数的函数下面是一个只接受位置参数的函数的例子:defthe_func(arg1:str,arg2:str,/):print(f'provided{arg1=},{arg2=}')#Thesework:the_func('num1','num2')the_func('num2','num1')#不会工作:TypeError:the_func()得到了一些位置参数作为关键字参数传递:'arg1,arg2'the_func(arg1='num1',arg2='num2')#不会工作:TypeError:the_func()得到了一些作为关键字参数传递的位置参数:'arg2'the_func('num1',arg2='num2')在函数定义中强制它前面的所有参数都是位置参数。这并不意味着/之后的所有参数都必须是kwarg-only;这些可以是位置和关键字。看到这里,你一定在想,你为什么要这个?这不会降低代码的可读性吗?我也认为你说的很正确,当定义一个非常具体的函数时,不需要关键字参数来指定它的函数。示例:defexceeds_100_bytes(x,/)->bool:returnx.__sizeof__()>100exceeds_100_bytes('a')exceeds_100_bytes({'a'})在这个例子中,它正在检查'a'的内存大小是否超过100话节。因为这个x的名字对我们来说并不重要,所以调用函数的时候不需要指定x='a'。比如我们最常用的len,如果你调用len(__obj=[])看起来有点傻,因为len是这样定义的deflen(__obj:Sized)->int:5,mixedandmatchedasAs例如,我们将查看前面讨论的len函数。此函数仅允许位置参数。我们将通过允许开发人员选择是否计算重复项来扩展此功能,例如将此关键字与kwargs一起传递:deflen_new(x,/,*,no_duplicates=False):if(no_duplicates):returnlen(list(set([aforeinx])))returnlen(x)要计算变量x的len,只能按位置传递x形参的参数,因为它前面有一个/。no_duplicate参数必须与关键字一起传递,因为它跟在*之后。让我们看看如何调用这个函数:print(len_new('aabbcc'))#返回6print(len_new('aabbcc',no_duplicates=True))#返回3print(len_new([1,1,2,2,3,3],no_duplicates=False))#返回6print(len_new([1,1,2,2,3,3],no_duplicates=True))#返回3#不会工作:TypeError:len_()得到了一些作为关键字参数传递的位置参数:'x'print(len_new(x=[1,1,2,2,3,3]))#不会工作:TypeError:len_new()takes1positionalargumentbut2weregivenprint(len_new([1,1,2,2,3,3],True))6.最后把它们放在一起下面的函数是一个非常极端的例子,说明如何结合前面讨论的所有技术:它强制前两个参数按位置传递,接下来的两个参数可以按位置传递并带有关键字,然后是两个仅关键字参数,然后我们使用**kwargs捕获其余未捕获的参数。defthe_func(pos_only1,pos_only2,/,pos_or_kw1,pos_or_kw2,*,kw1,kw2,**extra_kw):#不能传递kwarg<--|-->可以通过两种方式传递|-->只能通过kwarg传递print(f"{pos_only1=},{pos_only2=},{pos_or_kw1=},{pos_or_kw2=},{kw1=},{kw2=},{extra_kw=}")调用方法如下:#works(pos_or_kw1&pow_or_k2canbepassedpositionallyandbykwarg)pos_only1='pos1',pos_only2='pos2',pos_or_kw1='pk1',pos_or_kw2='pk2',kw1='kw1',kw2='kw2',extra_kw={}pos_only1='pos1',pos_only2='pos2',pos_or_kw1='pk1',pos_or_kw2='pk2',kw1='kw1',kw2='kw2',extra_kw={}pos_only1='pos1',pos_only2='pos2',pos_or_kw1='pk1',pos_or_kw2='pk2',kw1='kw1',kw2='kw2',extra_kw={'kw_extra1':'extra_kw1'}#不起作用,(pos1pos2不能通过kwarg传递)#the_func(pos_only1='pos1',pos_only2='pos2',pos_or_kw1='pk1',pos_or_kw2='pk2',kw1='kw1',kw2='kw2')#不起作用,(kw1和kw2不能按位置传递)#the_func('pos1','pos2','pk1','pk2','kw1','kw2')总结看起来很乱对吧,因为python在设计上是一门很松散的语言,没有那么多规范,用的人多了,用的方法也多了,就变成这样了。然后回到第一张图:deffunc(x,/,y,*,z,**k):(x,/,y,*,z,**k):是函数的参数。总共有四个参数:x:是一个常规参数,这意味着它可以按位置或按关键字传递。/,:是一个参数分隔符,用于将仅位置参数与其他参数分开。结合前面的x,这意味着x只能按位置传递。y:是另一个通用参数。*:是一个参数分隔符,用于将仅位置参数与仅关键字参数分开。意思是后面的z只能通过关键字传递。z:是一个仅限关键字的参数。**k:这是一个参数,它将所有剩余的关键字参数收集到名为“k”的字典中。这样解释够清楚了吧?虽然我们今天介绍的这个例子,看源码的时候并没有遇到这么复杂的情况,但其实面试的时候也问过(虽然我觉得没什么用),所以最好知道点东西,免得尴尬。如果你忘记了,这里可以教你一个workaround,你可以使用类似的答案:上面的参数传递在开发中并不常用,因为对于开发规范来说,要保证代码的可读性,我们按照这里的开发规范of是:1、在函数定义中尽量不要把可变位置参数args和可变关键字参数*kwargs放在一起,因为这样会使函数的调用方式不够直观。2.使用可变参数时,确保函数的行为是可预测的。上面的函数中python语法糖太多了,会造成理解函数参数的极大混乱,也就是可读性太差,我们是在做codereview(知道codereview是什么的就说吧,不要不懂就说组长检查)/组长在合并代码的时候会直接要求返工,所以我们在实际开发中不会用到这个。我看的开源代码,基本都是用**kwargs(这里举两个例子)。我还没有看到有人写过这么乱的代码。我认为编写这样的代码可能是开源的。也会被人吐槽(这里我可以自己扩展一下),所以我在学习的时候看过这些参数传递的规则,但是在实践中没见过,所以不太记得了.回到这篇文章,我们介绍了设计函数参数的所有方法,并学习了如何混合和匹配它们。虽然你可能在生活中永远不会用到以下内容,但了解一下还是有好处的,因为以防万一。https://avoid.overfit.cn/post/c2555b2ad4da4255814c94cfc3809efd作者:MikeHuls
