阅读本文大约需要8分钟。看完本文,仔细阅读代码,慎重返回。函数基础知识还可以。最好只是敲代码。1.函数基础简单地说,函数就是一组可以在程序中运行一次或多次的Python语句的组合。Python中的函数在其他语言中也称为过程或子例程,因此这些包装语句由函数名调用。有了函数,我们就可以大大减少复制粘贴代码的次数(相信很多人一开始都有这种体会)。我们可以提取相同的代码来制作一个函数,只需要在需要的地方调用它。那么,这就提高了代码的复用率,整体代码看起来更简洁,不那么臃肿。函数是Python中最基本的程序结构,用来最大限度地复用我们的代码;同时,函数可以将复杂的系统划分为可管理的部分,简化编程和代码重用。接下来,让我们看看函数是什么以及它应该如何定义。有两种定义函数的方法,即def和lambda关键字。1.函数定义我们总结一下为什么要用函数?最大化代码重用,最小化冗余代码;过程分解(拆卸)。将一个复杂的任务分解成多个较小的任务。函数定义的语法为:deffunc_name(arg1,arg2,arg3,...,argN):语句返回值根据上面的定义,可以简单描述为:Python中的函数有0个或多个参数,并且有几行语句和一个带有返回值的语句块(注意缩进)(返回值是可选的)。然后我们定义一个比较简单的函数,没有参数,进入ipython交互环境:In[1]:defhello():...:print('Leavemealone,theworld')...:Call(execute)thisfunction:In[2]:hello()Leavemealone,theworld我们发现hello()函数没有return语句。在Python中,如果没有显式执行return语句,那么函数的返回值默认为None。我们说过定义函数有两种形式,另一种形式是使用lambda定义的。使用lambda定义的函数是匿名函数,后面会解释,这里暂时不展示。2.函数参数在定义函数时,我们确定了参数的名称和位置,函数的接口定义就完成了。对于函数的调用者来说,知道如何传递正确的参数以及函数将返回什么值就足够了。函数内部的复杂逻辑被封装了,调用者不需要知道。Python的函数定义很简单,但是很灵活。除了通常定义的强制参数外,还可以使用默认参数、可变参数和关键字参数,这样函数定义的接口不仅可以处理复杂的参数,还可以简化调用者的代码。小白和0基础的可以加:456926667,里面收集了很多习题,提供学习平台。都是0基础的,大家可以一起讨论防坑1.默认参数默认参数让API简洁,但又不失灵活性。当一个参数有默认值时,调用时如果不传这个参数,就会使用默认值。definc(init,step=1):returninit+step#调用这个函数>>>inc(3)4>>>inc(3,2)5默认参数有个坑,就是非默认参数应该放在默认参数之前(否则Python解释器会报语法错误)。允许有多个默认参数,但默认参数需要放在参数列表的末尾。defappend(x,lst=[]):returnlst.append(x)这个函数有问题。(函数中的形参是全局变量?lst在append函数中叫做lst,但是在全局范围内,我们不知道lst叫什么名字。)修改后的函数为:defappend(x,lst=None):iflstisNone:lst=[]lst.append(x)returnlst一般来说,当默认参数可变时,需要特别注意范围问题,我们需要上面的技巧(不可变数据类型按值传递,可变数据类型按引用传递。)当前可变对象有list、dict、set、bytearray。默认参数很好用,但是如果使用不当,也会掉坑里。默认参数坑最大,演示如下:先定义一个函数,传入一个list,加一个END然后returndefadd_end(L=[]):L.append('END')returnL当我们正常调用它时,结果似乎不错:>>>add_end([1,2,3])[1,2,3,'END']>>>add_end(['x','y','z'])['x','y','z','END']当我们使用默认参数调用时,结果也是在开头:>>>add_end()['END']然而,当我们再次调用add_end()时,结果是错误的:>>>add_end()['END','END']>>>add_end()['END','END','END']原因解释如下:Python函数定义时,会计算默认参数L的值,即[],因为默认参数L也是一个变量,指向对象[],每次函数被调用时,如果改变了L的内容,默认参数的内容也改变了,不再是函数定义时的[]。因此,在定义默认参数时要记住一件事:默认参数必须指向不可变对象!修改上面的例子,我们可以使用None作为不可变对象:defadd_end(L=None):ifLisNone:L=[]L.append('END')returnL为什么要设计str,None这样的呢不可变对象?因为不可变对象一旦创建,对象内部的数据就无法修改,减少了修改数据带来的错误。另外,由于对象没有变化,在多任务环境下读取对象时不需要加锁,同时读取也没有问题。我们在写程序的时候,如果可以设计一个不变对象,那就尽量把它设计成一个不变对象。2.位置参数我们先写一个计算x^2的函数:defpower(x):returnx*x对于power(x)函数,参数x是一个位置参数。当我们调用power函数时,必须传入唯一的参数x:>>>power(5)25>>>power(15)225那么,如果我们要计算x^3呢?你可以再定义一个power3函数,但是如果你想计算x^4,x^5,x^n呢?我们不可能定义无限多的函数,我们可以将power(x)修改为power(x,n)来计算x^n,照你说的写:defpower(x,n):s=1当n>0时:n=n-1s=s*x返回s3。关键字参数可变参数允许我们传入0个或任意数量的参数,这些可变参数在函数调用时自动组装成一个元组。关键字参数允许你传入0或者任何包含参数名的参数,这些关键字参数会在函数内部自动组装成一个dict。例子如下:defperson(name,age,**kwargs):print('name:',name,'age:',age,'other:',kwargs)除了必须的参数name和age,函数person也接受关键字参数kwargs。调用该函数时,可以只传入必填参数:>>>person('LavenLiu',25)name:LavenLiuage:25other:{}也可以传入任意数量的关键字参数:>>>person('LavenLiu',25)name:LavenLiuage:25other:{}>>>person('Taoqi',25,city='Hebei')name:Taoqiage:25other:{'city':'Hebei''}>>>person('James',31,gender='M',job='NBAplayer')name:Jamesage:31other:{'gender':'M','job':'NBAplayerWhat'}关键字参数是为了什么?它可以扩展函数的功能。比如在person函数中,我们保证能收到name和age这两个参数,但是如果调用方愿意提供更多的参数,我们也可以收到。假设您正在执行用户注册功能。除了用户名和年龄是必填项外,其他都是可选的。使用关键字参数定义该函数即可满足注册要求。和可变参数类似,也可以先组装一个dict,然后把dict转成关键字参数传入:>>>kwargs={'city':'Hebei','job':'Test'}>>>person('Taoqi',25,**kwargs)name:Taoqiage:25other:{'city':'Hebei','job':'Test'}4.位置参数和关键字参数位置参数和关键字参数是函数调用中的一个概念。在将默认参数与关键字参数组合时很有用。关键字参数必须写在位置参数之后,否则会抛出语法错误。defminus(x,y):returnx-yminus(3,5)#位置参数,位置参数minus(5,3)#位置参数,位置参数minus(x=5,y=3)#关键字参数,关键字parameterminus(y=3,x=5)#关键字参数,关键字参数参数和关键字参数可以共存,但是关键字参数必须写在position参数之后。5.可变位置参数可变位置参数用*定义,在函数体中,可变位置参数是一个元组。可变位置参数。In[1]:deffn(*args):...:print(args)...:In[2]:fn((1,2,3,4))((1,2,3,4),)In[3]:tup01=(1,2,3,4)In[4]:fn(tup01)((1,2,3,4),)In[5]:fn(*tup01)(1,2,3,4)在python函数中,也可以定义可变参数。可变参数是指传入的参数个数是可变的。In[6]:defcacl(*numbers):...:sum=0...:forninnumbers:...:sum=sum+n*n...:returnsum...:In[7]:nums=[1,2,3]In[8]:cacl(*nums)#这里nums前面不加*是不是有问题?Out[8]:146.变量关键字参数变量关键字参数使用**定义。在函数体中,变量关键字参数是一个字典。variable关键字参数的key均为字符串,符合标识符定义规范。deffn(**kwargs):print(kwargs)dict01={'name':'LavenLiu','age':29}fn(**dict01)#fn(dict01)fn(name='LavenLiu',age=29){'name':'LavenLiu','age':29}{'name':'LavenLiu','age':29}变量位置参数只能作为位置参数调用,关键字参数只能作为关键字参数被调用变量位置参数必须在变量关键字参数之前In[18]:deffn(*args,**kwargs):...:print(args)...:print(kwargs)...:In[19]:fn(1,2,3,a=1,b=2)(1,2,3){'a':1,'b':2}In[20]:deffn(*args,x,y):...:print(args)...:print(x,y).??..:In[21]:fn(1,2,3,4)-------------------------------------------------------------------------TypeErrorTraceback(mostrecentcalllast)
