当前位置: 首页 > 后端技术 > Python

10分钟学会Python函数基础

时间:2023-03-26 10:53:21 Python

一、函数基础简单来说,函数就是程序中可以运行一次或多次的Python语句的组合。Python中的函数在其他语言中也称为过程或子例程,因此这些包装语句由函数名调用。有了函数,我们就可以大大减少复制粘贴代码的次数(相信很多人一开始都有这种体会)。我们可以提取相同的代码来制作一个函数,只需要在需要的地方调用它。那么,这就提高了代码的复用率,整体代码看起来更简洁,不那么臃肿。函数是Python中最基本的程序结构,用来最大限度地复用我们的代码;同时,函数可以将复杂的系统划分为可管理的部分,简化编程和代码重用。接下来,让我们看看函数是什么以及它应该如何定义。有两种定义函数的方法,即def和lambda关键字。函数定义首先总结一下为什么要使用函数?最大化代码重用,最小化冗余代码;过程分解(拆卸)。将一个复杂的任务分解成多个较小的任务。函数定义的语法为: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的函数定义很简单,但是很灵活。除了通常定义的强制参数外,还可以使用默认参数、可变参数和关键字参数,这样函数定义的接口不仅可以处理复杂的参数,还可以简化调用者的代码。默认参数默认参数使API简洁而不失灵活性。当一个参数有默认值时,调用时如果不传这个参数,就会使用默认值。definc(init,step=1):returninit+step调用此函数会报语法错误)。允许有多个默认参数,但默认参数需要放在参数列表的末尾。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这样的呢不可变对象?因为不可变对象一旦创建,对象内部的数据就无法修改,减少了修改数据带来的错误。另外,由于对象没有变化,在多任务环境下读取对象时不需要加锁,同时读取也没有问题。我们在写程序的时候,如果可以设计一个不变对象,那就尽量把它设计成一个不变对象。位置参数我们先写一个计算x^2的函数:defpower(x):returnx*x对于power(x)函数,参数x是一个位置参数。我们在调用power函数的时候,必须传入唯一的参数x:power(5)25power(15)225那么,如果我们要计算x^3呢?你可以再定义一个power3函数,但是如果你想计算x^4,x^5,x^n呢?我们不可能定义无限多的函数。我们可以把power(x)修改为power(x,n)来计算x^n,照你说的写就可以了:defpower(x,n):s=1whilen>0:n=n-1s=s*xreturn的关键字参数可变参数允许我们传入0个或任意数量的参数,这些可变参数在函数被调用时会自动组装成一个元组。关键字参数允许你传入0或者任何包含参数名的参数,这些关键字参数会在函数内部自动组装成一个dict。例子如下:defperson(name,age,**kwargs):print('name:',name,'age:',age,'other:',kwargs)除了必须的参数name和age,函数person也接受关键字参数kwargs。调用该函数时,只需传入必填参数:person('LavenLiu',25)name:LavenLiuage:25other:{}1.2.也可以传入任意数量的关键字参数:person('LavenLiu',25)name:LavenLiuage:25other:{}person('Taoqi',25,city='Hebei')name:Taoqiage:25other:{'city':'河北'}person('James',31,gender='M',job='NBAplayer')name:Jamesage:31other:{'gender':'M','job':'NBA球员'}1.2.3.4.5.6.7.8。关键字参数有什么用?它可以扩展函数的功能。比如在person函数中,我们保证能收到name和age这两个参数,但是如果调用方愿意提供更多的参数,我们也可以收到。假设您正在执行用户注册功能。除了用户名和年龄是必填项外,其他都是可选的。使用关键字参数定义该函数即可满足注册要求。与可变参数类似,也可以先组装一个dict,然后将dict转为关键字参数:kwargs={'city':'Hebei','job':'Test'}person('Taoqi',25,**kwargs)name:Taoqiage:25other:{'city':'Hebei','job':'Test'}位置参数和关键字参数位置参数和关键字参数是函数调用时的概念。在将默认参数与关键字参数组合时很有用。关键字参数必须写在位置参数之后,否则会抛出语法错误。defminus(x,y):returnx-yminus(3,5)#位置参数,位置参数传递minus(5,3)#位置参数,位置传递参数minus(x=5,y=3)#关键字参数,关键字参数minus(y=3,x=5)#关键字参数、关键字参数和关键字参数可以共存,但是关键字参数必须写在position参数之后。可变位置参数可变位置参数用*定义,在函数体中,可变位置参数是一个元组。可变位置参数。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]:14Variablekeywordparameters变量关键字参数使用**定义,在函数体中,变量关键字参数是一个字典。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}可变位置参数只能作为带可变关键字的位置参数调用argumentscanonlybecalledaskeywordarguments变量位置参数必须先于变量关键字参数In[18]:deffn(args,*kwargs):...:print(args)...:print(kwargs)...:在[19]:fn(1,2,3,a=1,b=2)(1,2,3){'a':1,'b':2}在[20]:deffn(*args,x,y):...:print(args)....:print(x,y)...:In[21]:fn(1,2,3,4)TypeErrorTraceback(最近的calllast)in()---->1fn(1,2,3,4)TypeError:fn()missing2requiredkeyword-onlyarguments:'x'and'y'In[22]:fn(1,2,x=3,y=4)(1,2)34个可变参数post可变参数不与默认参数一起出现参数组合在Python中定义了一个函数。您可以使用强制参数、默认参数、可变参数和关键字参数。这4种参数可以一起使用,也可以只使用其中的一部分,但请注意参数定义的顺序必须是:必需参数、默认参数、可变参数和关键字参数例如,定义一个函数,它包含以上四个参数:deffunc(a,b,c=0,args,*kwargs):...print('a=',a,'b=',b,'c=',c,'参数=',args,'kwargs=',kwargs)函数调用时,Python解释器根据参数位置和参数名自动传入相应的参数func(1,2)a=1b=2c=0args=()kwargs={}func(1,2,c=3)a=1b=2c=3args=()kwargs={}func(1,2,3,'a','b')a=1b=2c=3args=('a','b')kwargs={}func(1,2,3,'a','b',x=99)a=1b=2c=3args=('a','b')kwargs={'x':99}最神奇的是我们还可以通过元组和字典调用这个函数:args=(1,2,3,4)kwargs={'x':99}func(args,*kwargs)a=1b=2c=3args=(4,)kwargs={'x':99}所以,对于任何函数,你可以像func(args,*kwargs)一样调用它,不管它的参数是如何定义的。参数解构参数解构发生在调用函数时,可变参数发生在定义函数时。参数解构有两种形式,一种是位置参数解构,另一种是关键字参数解构。参数结构的两种形式:位置参数解构,使用星号。解构后的对象是一个可迭代对象,解构后的结果是一个位置参数。关键字参数解构,使用两个星号。解构后的对象是字典,解构后的结果是关键字参数。位置参数解构的一个例子:In[23]:deffn(a,b,c):...:print(a,b,c)...:In[24]:lst=[1,2,3]In[25]:fn(lst[0],lst[1],lst[2])123也可以用下面的形式调用In[26]:fn(*lst)#调用这个方法参数解构123*可以将线性结构解包成位置参数lst=[1,2,3,4]fn(*lst)#->fn(lst[0],lst[1],lst[2],lst[3])TypeError:fn()takes3positionalargumentsbut4weregiven此处报错。本来这个函数只能接收3个位置参数,而lst有四个元素。参数解构后变成指定了4个参数,所以报错。接下来看字典解构的例子:In[27]:d={'a':1,'b':2,'c':3}In[28]:fn(**d)123**字典可以解构为关键字参数参数解构发生在函数调用时。解构时,线性结构的解构是位置参数,字典的解构是关键字参数。传参顺序:位置参数,线性结构解构;关键字参数,字典解构。除非你真的知道你在做什么,否则尽可能少地使用这两种解构。In[29]:deffn(a,b,c,d):...:print(a,b,c,d)...:In[30]:fn(0,[2],c=1、*{'d':3})0213参数槽(keyword-onlyparameter)在Python3中引入。deffn(a,b,c):print(a,b,c)fn(a=1,b=2,c=3)如果要强制传入参数为关键字参数:deffn(*,a,b,c):print(a,b,c)fn(1,2,3)Traceback(最后一次调用):文件“”,第1行,在fn(1,2,3)TypeError:fn()takes0positionalargumentsbut3weregivenfn(a=1,b=2,c=3)123*后面的参数必须以关键字参数的形式传递,称为parameter投币口。参数槽通常与默认参数一起使用。deffn(a,b,*,x,y):print(a,b)print(x,y)fn(1,2,3,4)Traceback(最后一次调用):文件“",line1,infn(1,2,3,4)TypeError:fn()takes2positionalargumentsbut4weregivenfn(1,2,x=3,y=4)1234fn(1,2,**{'x':3,'y':4})1234deffn(a,b,*):print(a,b)deffn(a,b,*):...print(a,b)File"",line1SyntaxError:namedargumentsmustfollowbare*几个例子:deffn01(*,x=1,y=5):print(x)print(y)fn01()15deffn02(x=1,*,y):print(x)print(y)fn02(y=3)参数槽的坑:*后面一定要有参数。当非命名参数有默认值时,命名参数可以省略Defaultvalue默认参数应该在每个参数的末尾。使用参数槽时,不能使用可变位置参数。可变键参数必须放在命名参数之后。3.高级用法递归函数在函数内部,可以调用其他函数。如果函数在内部调用自身,则该函数是递归的。deffact(n):ifn==1:return1returnn*fact(n-1)使用递归函数的好处是逻辑简单明了,缺点是调用太深会导致栈溢出。针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归其实就相当于循环,没有循环语句的编程语言只能通过尾递归来实现循环。匿名函数lambdapython使用lambda来创建匿名函数。lambda只是一个表达式,函数体比def要简单的多。lambda的主体是一个表达式,而不是代码块。lambda表达式只能封装有限的逻辑。lambda函数有自己的命名空间,不能访问自己的参数列表之外或全局命名空间中的参数。虽然看起来lambda函数只能写一行,但并不等同于C或C++的内联函数。后者的目的是通过在调用小函数时不占用栈内存来提高运行效率。fib=lambdan,x=0,y=1:xifnotnelsefib(n-1,y,x+y)print(fib(20))Python函数中的多态一个操作的意义取决于typeofoperationobject:deftimes(x,y):returnx*ytimes(2,4)8times('Python',4)#passedadifferentdatatype'PythonPythonPythonPython'4.总结Python函数有非常灵活的参数形式不仅可以实现简单的调用,还可以传入非常复杂的参数。默认参数必须使用不可变对象。如果是可变对象,运行就会出现逻辑错误!注意定义可变参数和关键字参数的语法:*args为可变参数,args接收一个元组;**kwargs是关键字参数,kwargs接收的是一个dict。以及调用函数时如何传入可变参数和关键字参数的语法:可以直接传入可变参数:func(1,2,3),也可以先组装一个list或tuple,再通过传入参数:函数((1、2、3));关键字参数可以直接传入:func(a=1,b=2),也可以先组装成dict,再通过kwargs传入:func({'a':1,'b':2}).使用args和*kwargs是Python的习惯用法。当然也可以使用其他参数名,但最好使用成语。