循序渐进Python程序设计 课件 第4章 函数应用_第1页
循序渐进Python程序设计 课件 第4章 函数应用_第2页
循序渐进Python程序设计 课件 第4章 函数应用_第3页
循序渐进Python程序设计 课件 第4章 函数应用_第4页
循序渐进Python程序设计 课件 第4章 函数应用_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

01020304第4章函数应用4.1函数概述4.2自定义函数4.3函数的特殊形式4.4变量作用域054.5Python内置函数064.6综合应用案例4.1函数概述4.1.1函数的分类4.1.2函数的主要作用第4章函数应用4.1.1函数的分类4.1函数概述(1)内置函数。即Python解释器内置的函数,无需额外导入模块即可直接使用。这些函数提供了一些常用的基本功能,覆盖了数据类型转换、数学运算、序列操作等多个方面。例如print、input、len、range等,都是Python内置函数,可以直接使用。(2)标准库函数。安装Python环境时,会一同安装若干标准库/模块,其中的函数需要通过import导入后才能使用。如math模块中的sin、cos函数;random模块的randint、sample函数。(3)第三方库函数。Python社区提供的第三方库,需要安装并导入才能使用库中的函数,如Matplotlib库、NumPy库等。(4)自定义函数。这是开发者根据实际需求,自行定义的、用于实现特定功能的函数。1.按来源分类第4章函数应用4.1.1函数的分类4.1函数概述(1)有返回值函数。函数调用后能返回数据。可以是各种数据类型,如整数、字符串、列表、字典等,也可以是另一个函数。例如,input函数调用后的返回值就是通过键盘输入的数据。(2)无返回值函数(返回None的函数)。函数定义中没有显式的return语句,或者return语句后面没有任何值,这种函数的返回值为None。通常用于执行一些操作,而不需要返回具体的结果。例如,print函数执行后无返回值。3.按返回值分类2.按参数类型分类(1)无参函数。函数定义时不包含参数,调用时也无需传入参数。这种函数通常用于执行一些固定的操作,不依赖外部传入的数据。(2)有参函数。函数定义时包含参数,调用时需要传入相应的值。第4章函数应用4.1.1函数的分类4.1函数概述(1)全局函数。全局函数是指定义在模块全局作用域中的函数,可以在该模块的其他地方以及导入该模块的其他模块中调用。(2)局部函数。局部函数是定义在其他函数内部的函数,只能在其所在的外部函数内部调用。4.按函数作用域分类第4章函数应用4.1.2函数的主要作用4.1函数概述函数是编程中提升代码质量的核心工具,它能有效减少重复代码,通过封装功能实现“一次编写,多次调用”。例如像print这样的内置函数,开发者无需了解其内部实现,只需通过直观的函数名和参数就能完成输出操作。良好的函数命名和文档字符串能显著提升代码可读性,让其他开发者快速理解功能逻辑。更重要的是,函数将复杂系统拆分为独立的模块单元,每个模块专注单一功能,这种模块化设计不仅使代码结构清晰,更让后续的维护升级变得高效——只需修改特定函数内部实现,而无需调整调用逻辑。从基础运算到复杂业务处理,合理的函数设计都是构建可维护、易扩展程序的关键。4.2自定义函数4.2.1函数的定义4.2.2函数的调用4.2.3函数参数的类型与参数传递第4章函数应用4.2.1函数的定义4.2自定义函数在Python中,函数定义的基本格式为:def函数名(函数参数):

函数文档字符串

函数体defmyfun(a:(list,tuple,set),b:int)->int: #定义函数#函数体【例4.1】格式说明:(1)函数名可用英文字母、数字和下划线组成。数字不能作为首字符。不能与Python已有的关键字同名。函数名一般需要体现函数的功能,方便理解和维护。(2)函数参数可以缺省,表示没有参数,也可以有一个或者多个参数。这些参数本质上就是变量,可以在函数体内使用。(3)函数文档字符串是用三引号界定的字符串,可以缺省但推荐使用。必须写在函数开始部分(代码注释除外),用于对自定义函数的功能、参数使用给出简要说明。(4)函数体是函数功能的实现代码,必须与def行保持右缩进(默认4个空格)。当函数体比较简短时,也可以将函数体与def写在同一行。(5)在函数体中,可以使用return语句设置函数的返回值。(6)可以为部分或全部参数添加类型注解,也可为函数返回值类型添加注解,其格式为:def函数名(参数:数据类型)->函数返回值类型。参数a类型注解为(list,tuple,set),表示a的数据类型可以是列表、元组或set集合。参数b类型注解为int,表示b的数据类型为int。“->int”为函数返回值类型注解,表示函数返回值类型为int。如果函数无返回值,则返回值类型可注解为None。第4章函数应用4.2.1函数的定义4.2自定义函数【例4.2】defadd(a): #定义函数"""对可迭代对象a的数值元素求和""" #函数文档字符串try:iter(a) #如果a不是可迭代对象,会触发异常except:return0 #a不是可迭代对象,返回函数值0,终止s=0 #和值初值foreina: #遍历atry:s+=e #如果e不是数值,会触发异常except:returns #e不是数值,返回函数值并终止finally:continue #无论是否触发异常,继续循环returns #返回函数值,终止print(add([1,2,"good",3]))程序执行后的输出结果为6而不是3。这是因为,add([1,2,"good",3])调用函数时,a=[1,2,"good",3]。当e遍历a取值到"good"时,s的值是3(=1+2)。由于s+=e会触发异常,导致执行“except:returns”。根据try语句的运行机制,该语句不会终止函数执行,而是会执行finally后的子句,导致循环继续。【练一练4.1】(1)删除上述代码中的finally语句,观察程序执行结果并分析原因。(2)为上例定义的函数的参数和函数返回值类型添加注解。第4章函数应用4.2.2函数的调用4.2自定义函数函数调用格式:函数名([数据])使用说明:函数调用时,“数据”会传递给函数定义时的相应参数。因此,数据的个数及位置必须满足参数的要求。【练一练4.2】分别使用(2,3,"fine",4,5)、{2,3,"fine",4,5}、2,3,"fine",4,5和23等数据调用例4.2中定义的函数,观察输出结果并分析原因。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数1.形参和实参的区别在定义函数时,函数名后面圆括号中定义的参数是形式参数,简称形参。在调用函数时,函数名后面圆括号中传入的数据是实际参数,简称实参。形参是函数需要传递的参数,是变量。实参是调用函数时传递的参数,可以是常量、变量或表达式。例如,“defmyfun(a,b):returna**2+b**2”定义了函数myfun,其中a、b就是形参。使用语句“c=myfun(3,4)”调用函数,3和4就是实参。该语句的执行过程是:(1)实参3传递给形参a,实参4传递给形参b,即a=3、b=4。(2)执行函数体,返回函数值a**2+b**2,即3**2+4**2,值为25。因此,编写函数时,形参可以理解为已知数据,在函数体中可直接利用形参的数据进行运算或处理。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数2.函数形参的类型(1)必选参数。必选参数是调用函数时必须传递数据的参数。如“defavg(a,b,c):return(a+b+c)/3”定义的avg函数的三个参数都是必选参数(也称必需参数)。(2)默认参数。在定义函数时,为参数设置了默认值的参数称为默认参数。函数调用时可以不为默认参数传递数据(这时会使用函数定义时设置的默认值),也可以传递数据。【例4.3】defadd(a,b=10):returna+bx=add(5) #x的值为15。b未传递值,使用默认值10x=add(5,3) #x的值为8。b传递值3,不使用默认值函数定义中,既有必选参数,又有默认参数时,必选参数必须在默认参数之前,否则会触发异常。例如,按照“deffun(a=0,b)”方式定义函数是错误的:b为必选参数,不能放在默认参数a之后。【特别提示】第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数2.函数形参的类型(3)可变参数。也称不定长参数,是指参数的个数不确定,可以是0个、1个或多个。有两种形式:使用星号(*)标识,称为可变位置参数。函数调用时,传递的多个数据会打包为元组。【练一练4.3】函数muladd定义如下,分别输出muladd(3)、muladd(1,2,3)和muladd()的函数值,并分析原因。defmuladd(*a):s=0forxina:s+=xreturns第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数2.函数形参的类型使用双星号(**)标识,称为可变关键字参数。调用函数时,传递的多个数据会打包为字典。每个数据必须按“a=v”方式传递,称为按关键字传递。其中a会转换为字符串并作为字典的键,v是该键的键值。【例4.4】defkeyadd(**a):s=0forxina:s+=a[x]returnsa=keyadd(x=1,y=2,z=3)+keyadd(a=4,b=5)+keyadd()print(a)执行“keyadd(x=1,y=2,z=3)”时,按关键字传递的三个参数会打包为字典:a={"x":1,"y":2,"z":3},函数返回值为a各键的键值和6。类似地,执行“keyadd(a=4,b=5)”时,函数返回值为字典{"a":4,"b":5}的各键值和9。执行“keyadd()”时,函数返回值为空字典的键值和0。因此,print(a)的输出为15。【练一练4.4】将“print(a)”添加到keyadd函数函数体的开始行,观察代码的输出效果,并分析原因。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数2.函数形参的类型【例4.5】defmuladd(a,b=0,*c,**d):s=a+bforxinc:s+=xforxind:s+=d[x]returnsa=muladd(1,2)+muladd(3,4,x=5)+deyadd(6,7,8,x=9,y=10)print(a)执行muladd(1,2)时函数形参的值分别为a=1、b=2、c=()、d={},函数值=3。执行muladd(3,4,x=5)时函数形参的值分别为a=3、b=4、c=()、d={'x':5},函数值为12。执行deyadd(6,7,8,x=9,y=10)时函数形参的值分别为a=6、b=7、c=(8,)、d={'x':9,'y':10},函数值为40。因此print(a)的输出值为55。【练一练4.5】将“print(f"a={a},b={b},c={c},d={d}")”添加到muladd函数函数体的开始行,观察代码的输出效果,并分析原因。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数3.函数实参的传递方式根据各类形参的特点,有按位置传递和按关键字传递两种实参传递方式。(1)按位置传递。按位置传递是指函数调用时按照参数定义的顺序依次传入数据的参数。即将第1个实参传递给第1个形参,第2个实参传递给第2个形参,依次类推。这类参数也称为位置参数。(2)按关键字传递。按关键字传递是指按“参数名=值”的方式传递参数。这类参数也称为关键字参数。【特别提示】(1)传递参数时,必须先按位置传递参数,再按关键字传递。(2)函数定义中,命名为“/”或“*”的参数已经不具备参数意义。“/”表示之前的参数必须按位置传递,“*”表示之后的参数必须按关键字传递。如果没有“/”和“*”限制,则参数既可以按位置传递,也可按关键字传递。但可变关键字参数必须按关键字传递。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数3.函数实参的传递方式(3)“按关键字传递”与“可变关键字参数”有着本质的区别:“按关键字传递”是指调用函数时参数的传递方式;而“可变关键字参数”是指函数定义时形参的类型。“可变关键字参数”必须按关键字传递。必需参数、默认参数可以按关键字传递,也可以按位置传递,但受“/”和“*”制约。(4)必需参数、默认参数按关键字传递时,传递的顺序可以与函数定义时的形参顺序不一致,但关键字必须与形参同名。如果没有同名的形参,Python解释器会视其为可变关键字参数。如果函数定义中没有定义可变关键字参数,则触发异常。(5)按关键字传递可变关键字参数时,关键字不能与已定义的函数形参同名。否则,按位置参数处理。(6)传递参数时,可使用“*”标识列表或元组,表示将其元素解包为传递的多个参数。也可使用“**”标识字典,表示将其元素解包为多个关键字参数。这里要注意与形参的可变参数区别。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数3.函数实参的传递方式【例4.6】“deffun(a,b=0,/,*,c,d):returna+b+c+d”定义函数,则有:(1)x=fun(a=3,c=4,d=5):触发异常。因为“/”限制了a、b必须按位置传递(b可缺省)。(2)x=fun(3,d=4,c=5):不会触发异常,x的值为12。这里b未传递值,形参c、d按关键字传递顺序已经改变,都是允许的。(3)x=fun(3,4,5,6):触发异常。因为"*"限制了c、d必须按关键字传递。(4)x=(3,4);y={'c':5,'d':6};z=fun(*x,**y)):不会触发异常,z的值为18。这里x被解包后传递给位置参数a和b,y被解包后传递给参数c和d。即等效于z=fun(3,4,c=5,d=6)。(5)x=(3,4);y={'c':5,'e':6};z=fun(*x,**y)):会触发异常。其等效于z=fun(3,4,c=5,e=6)。但形参中没有名为e的参数。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数3.函数实参的传递方式【练一练4.6】函数fun定义如下,试分析“x=(3,4);y={'c':5,'d':6};z=fun(*x,**y)”的执行效果,并分析原因deffun(a,b=0,/,*,c,**d):s=a+b+cforxind:s+=d[x]returns将字典解包传递给函数形参时,遵循以下规则:(1)如果字典的键与必需参数(或默认参数)同名,则将键值传递给位置参数(或默认参数)。如果已要求这些参数按位置传递,则会触发异常。(2)如果字典的键不与必需参数和默认参数同名,则将键和键值传递给可变关键字参数。如果函数形参中未定义可变关键字参数,则会触发异常。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数4.函数形参对实参变量的影响在Python中,所有变量(包括函数形参),本质上都是对对象的引用。函数调用时,如果函数把变量作为实参传递给函数的形参,那么,形参和实参引用的是同一数据对象。在函数体内改变了形参的数据,对实参变量的数据是否产生影响呢?先看以下的示例。【例4.7】deffun(a:int,b:list,c:list):a+=3;print(f"a={a}")b[0]+=1;print(f"b={b}")c=c+[1,2,3];print(f"c={c}")returna+sum(b)+sum(c)x=6;y=[11,16];z=[20,21,22]v=fun(x,y,z)print(f"x={x}")print(f"y={y}")print(f"z={z}")print(f"v={v}")(1)调用函数前,变量x=6。调用函数时,x作为实参变量传递给函数形参a。这时变量a和x引用的是同一对象6。执行a+=3后,形参的引用被改变,输出a=9。调用函数后,输出x=6,表明x的引用并没有被形参改变。(2)调用函数前,变量y=[11,16]。调用函数时,y作为实参变量传递给函数形参b。这时变量b和y引用的是同一对象[11,16]。执行b[0]+=1后,b的引用并没有改变,只是改变了元素b[0]的引用。由于b和y仍然引用的是同一对象,因此,y[0]的引用也被改变。所以,函数调用后,输出y=[12,16]。(3)调用函数前,变量z=[20,21,22]。调用函数时,z作为实参变量传递给函数形参c。这时变量c和z引用的是同一对象[20,21,22]。执行c=c+[1,2,3]后,形参c的引用已改变,函数调用后,z的引用并没有改变,故输出z=[20,21,22]。第4章函数应用4.2.3函数参数的类型与参数传递4.2自定义函数4.函数形参对实参变量的影响综上所述,有以下结论:(1)调用函数时,实参变量与形参变量引用的是同一对象。(2)形参变量可以改变引用,但不可能改变实参变量的引用。(3)当可变对象(如列表)作为实参传递时,函数内对形参的元素修改会同步影响实参,因为形参与实参共享同一对象的引用。这一机制允许通过函数直接修改可变实参的内容。【练一练4.7】定义函数getinfs(a,b),返回值为None。其功能是:通过参数b得到列表a(元素均为整数)的最大值、最小值和均值。如执行“b=[];getinfs([68,92,70,63,88],b)”后,b的值为[92,63,76.2]。4.3函数的特殊形式4.3.1匿名函数4.3.2函数嵌套4.3.3函数递归第4章函数应用4.3.1匿名函数4.3

函数的特殊形式lambdaarg1,arg2,...:exp定义匿名函数的语法格式为:其中:arg1,arg2,...为参数。可以是0个、1个或多个。exp是一个表达式,是匿名函数的返回值。匿名函数的使用有两种方式:(1)以表达式方式使用。在表达式中完成匿名函数的定义和调用。这时匿名函数为临时性的函数,不能象普通函数那样被重复调用。【例4.8】x,y,z=1,2,3f=x+(lambdam,n:m*n)(y,z)print(f)#输出f的值为7在语句“f=x+(lambdam,n:m*n)(y,z)”中,先由“lambdam,n:m*n”定义了匿名函数,表明调用该函数时需传递二个参数,函数的返回值为这两个参数的乘积。然后在表达式中直接调用该匿名函数:(lambdam,n:m*n)(y,z),即传递了y和z两个参数,这时函数的返回值为6。从而f的值为1+6=7。第4章函数应用4.3.1匿名函数4.3

函数的特殊形式(2)先将匿名函数的定义保存到一个变量,再调用匿名函数。将匿名函数的定义保存到一个变量,这个变量为函数对象的引用。可以通过这个变量多次调用匿名函数。【例4.9】#求两个数的余数f=lambdax,y:x%yprint(f(15,2),f(32,8))#输出10a=[45,13,62,89]a.sort(key=lambdax:x%10)print(a)#执行后a的值[62,13,45,89]在语句“f=lambdax,y:x%y”中,先由“lambdax,y:x%y”定义了匿名函数,再由变量f引用该函数对象。然后通过语句“print(f(15,2),f(32,8))”二次调用变量f引用的函数。在语句“a.sort(key=lambdax:x%10)”中,先由“lambdax:x%10”定义了匿名函数,再由sort函数的参数key引用该匿名函数。sort函数使用key参数指定的函数完成对列表a元素的排序:即将a的元素按匿名函数的函数值排序。第4章函数应用4.3.2函数嵌套4.3

函数的特殊形式定义函数求解问题时,可能需要解决几个相关的子问题。这些子问题可以另外定义函数,必要时也可以写在函数的内部。Python允许函数的嵌套定义,即在函数内部可以再定义另外一个函数。函数嵌套是编程中非常常见的做法,它使得代码更加模块化,易于理解和维护。【例4.10】计算[3,n](n>3)上质数的个数。defcountp(n):defisp(m):k=3whilek<m:ifm%k==0:return0k+=2return1k,s=3,0whilek<=n:s+=isp(k)k+=2returnsprint(countp(100))函数countp(n)用于计算[3,n](n>3)上质数的个数。其基本思路是:(1)k=3(依次对[3,n]上的每个奇数k进行判断)、s=0(质数个数初值)。(2)如果k>n,转步骤(4)。否则,更新s的值:s+=isp(k)。这里调用了嵌套函数isp。如果k是质数,isp(k)的值为1。否则,isp(k)的值为0。(3)k增加2,转步骤②。(4)输出s的值(质数个数)。【练一练4.8】(1)尝试在countp函数的外部调用isp函数,分析调用结果产生的原因。(2)将isp函数写在countp函数之外再调用上例中的countp函数,比较执行效果是否相同。第4章函数应用4.3.3函数递归4.3

函数的特殊形式函数递归指在函数的定义中使用函数自身的方法,即函数直接或间接地调用自身。函数递归的典型过程是:(1)问题分解。它把问题的求解过程分解为若干个性质相同大的较小问题的求解过程。当分解达到边界条件时,就停止分解。(2)回推。它是对分解的过程进行逆处理。回推时,从最小的问题入手,依次求得较大问题的解,最终得出原问题的解。利用函数递归求解问题的经典模型是:对于一个与正整数n有关的问题,可以定义函数f(n)求得问题的解。如果已知初始条件(即f(k)已有解,k通常为0或1),且可通过f(n-1)求得f(n)的解,则可使用函数递归求解问题。第4章函数应用4.3.3函数递归4.3

函数的特殊形式【例4.11】计算正整数n的阶乘。“计算正整数n的阶乘”满足函数递归求解问题的经典模型。设函数f(n)可以求n的阶乘,则有:f(1)=1(1的阶乘等于1),且f(n)=n*f(n-1)(n>1。即可以通过f(n-1)求得f(n)的值)。故可以定义以下函数求正整数n的阶乘。deff(n):ifn==1:return1else:returnf(n-1)*n可以看出,函数递归包括二个过程:前3步是递推过程,为计算f(4)的值,需先依次计算f(3)、f(2)和f(1)的值。而f(1)的值是已知的,因此从第4步开始,逐层回溯,依次计算出f(2)、f(3)、f(4)的值。后6步称为回溯过程。【练一练4.9】利用函数递归求1+2+3+...+n的值。【练一练4.10】设数列F(n)满足:F(0)=0,F(0)=1,F(n)=F(n-1)+F(n-2)(n≥2,n为整数)。试分别使用函数递归和非递归的方法求F(40),并比较两者在执行时间上的差异。4.4变量作用域4.4.1作用域类型4.4.2使用global和nonlocal关键字声明变量第4章函数应用4.4.1作用域类型4.4变量作用域1.局部作用域Python中的作用域共有4类:在函数内部定义的变量具有局部作用域(LocalScope),它们只能在函数内部被访问和修改。当函数执行结束后,这些变量会被销毁,内存空间被释放。这类变量称为局部变量。局部变量是在函数内定义的变量,也包括函数的形参,在函数执行时才会被创建,可以用来存储函数内部临时使用的数据,只能在定义它的函数内部使用。2.嵌套作用域当一个函数嵌套在另一个函数内部时,内层函数可以访问外层函数的变量,这种外层函数的作用域就是嵌套作用域(EnclosingScope),也称为闭包作用域。这类变量称为非局部变量。因此,非局部变量是在嵌套函数的外层函数中定义,也称为外层函数的局部变量,在嵌套函数内使用的变量。第4章函数应用4.4.1作用域类型4.4变量作用域3.全局作用域Python中的作用域共有4类:4.内置作用域在模块的顶层定义的变量具有全局作用域(GlobalScope),它们可以在整个模块内被访问,这类变量称为全局变量。内置作用域(Built-inScope)是Python解释器内置的一些变量和函数的作用域。如

print、len

等函数以及

True、False

等数据都属于内置作用域,它们在整个程序中都可以直接使用。由Python解释器本身定义的变量,存在于内置作用域中,在整个Python环境中都可以直接使用,称为内置变量。如“__name__”,用于存储当前模块的名称,“__doc__”,用于存储对象的文档字符串,两者都是内置变量。【练一练4.11】运行以下代码,观察输出结果并分析原因。a=6print(a+x-2)x=3print(a+x-2)第4章函数应用4.4.2使用global和nonlocal关键字声明变量4.4变量作用域全局变量的作用域是所在模块,这意味着在该模块定义的函数中,也可以使用全局变量。但是,在默认情况下,函数内只能使用全局变量的值,不能修改全局变量的值。1.使用global关键字声明全局变量在函数内可使用global关键字声明全局变量,且遵循以下原则:(1)函数内只使用不修改全局变量的值,不必使用global声明,这是Python的默认行为。(2)函数内需修改全局变量的值,必须先使用global声明。(3)函数内如果不使用global声明,而直接修改全局变量的值,则将该变量作局部变量处理。这时局部变量只是一个与全局变量同名的变量,本质上与全局变量没有任何关系。(4)如果函数中先使用了全局变量,但并没有改变它的值,那么,不能再使用global关键字声明。第4章函数应用4.4.2使用global和nonlocal关键字声明变量4.4变量作用域【例4.12】deffun(c):globalba=2;b=4;c+=3returna+b+ca=5;b=1;c=2d=fun(c)print(a,b,c,d)在函数fun外,定义了变量a、b、c、d,均为全局变量。在函数fun内,变量b使用了global声明,为全局变量,且可在函数内重新赋值。函数内的其他变量a、c均为局部变量。因此,函数执行后,全局变量b的值已修改,而全局变量a、c虽然与函数内的局部变量同名,但没有任何关系,值不变。程序的最后输出为54211。2.使用nonlocal关键字声明非局部变量在函数嵌套定义中,内函数可使用nonlocal关键字声明非局部变量,且遵循以下原则:(1)内函数只使用不修改非局部变量的值,不必使用nonlocal关键字声明,这是Python的默认行为。(2)内函数需修改非局部变量的值,必须先使用nonlocal关键字声明。第4章函数应用4.4.2使用global和nonlocal关键字声明变量4.4变量作用域2.使用nonlocal关键字声明非局部变量(3)内函数如果不使用nonlocal关键字声明,而直接修改非局部变量的值,则将该变量作内函数的局部变量处理。这时它只是一个与非局部变量同名的变量,本质上与非局部变量没有任何关系。(4)如果内函数中先使用了非局部变量,但并没有改变它的值,那么,不能再使用关键字nonlocal声明。【例4.13】defgetsum(*a):x=5;y=2defjudge(a):nonlocalxx+=1;y=3returnsum(a)+x+yreturnjudge(a)x=1;a=getsum(1,2,3,4,x)print(x,a)在函数外定义的变量x、a均为全局变量,且未在函数中使用global声明,故getsum函数内的变量x、a、y都是局部变量。在judge函数中,使用nonlocal声明了变量x,表明x是非局部变量。但a、y是该函数内的局部变量,不是非局部变量。因此,执行“a=getsum(1,2,3,4,x)”后全局变量x的值未变。调用getsum(1,2,3,4,x)时,函数getsum的参数a=(1,2,3,4,1),返回值为judge(a),即judge((1,2,3,4,1)),值为20。程序输出为“120”。【练一练4.12】在judge函数中使用global声明全局变量x,观察输出结果的变化,并分析原因。4.5内置函数4.5.1数学函数4.5.2可迭代对象处理函数第4章函数应用4.5.1数学函数4.5

内置函数1.eval函数eval函数的主要作用是将字符串作为Python表达式进行解析和执行,并返回执行结果。语法格式:eval(source,globals,locals)参数说明:(1)source:为字符串,代表要被解析和执行的Python表达式。(2)globals:可选参数,是一个字典,用于指定全局命名空间。如果提供了该参数,eval函数会在这个字典所代表的全局命名空间中查找变量和函数。如果不提供,默认使用当前的全局命名空间。(3)locals:可选参数,是一个字典,用于指定局部命名空间。如果提供了该参数,eval函数会在这个字典所代表的局部命名空间中查找变量和函数。如果不提供,默认使用当前的局部命名空间。

【特别提示】

1、eval函数有3个基本应用:计算表达式的值。如:eval("3+8*2")的值为19。将数值字符串转换为数值。如:eval("23.56")的值为23.56、eval("432")的值为432。将数值序列字符串转换为元组。如:eval("2,3,4")的值为(2,3,4)。2、命名空间可以理解为一个存储变量名与对象之间映射关系的容器,通常为字典。第4章函数应用4.5.1数学函数4.5

内置函数1.eval函数【例4.14】假定s4t5p1.py文件的内容如下,试运行该代码并观察输出结果。x=1;y=2z=eval("x+y*2")print(z)print(locals())print(globals())执行语句“x=1;y=2”后,全局命名空间和局部命名空间中会同时添加变量x和y。因此,语句“z=eval("x+y*2")”执行时,eval函数会先从局部命名空间中查找变量x和y,将它们的值代入到表达式x+y*2中计算,并返回计算结果5。【练一练4.13】执行以下代码,观察输出结果,并分析原因。deffun(a,b):print(locals())returna*b+a+bx=1;y=2z=eval("fun(x,y)");print(z)z=eval("fun(a,b),locals={'a':5,'b':6}");print(z)print(globals())第4章函数应用4.5.1数学函数4.5

内置函数2.round函数round主要用于对数值进行四舍五入操作。语法格式:round(x[,n])参数说明:返回x的四舍五入值,n为舍入到小数点后的位数。如果n为负整数或0,则舍入到小数点之前。缺省n时n=0。由于浮点数精度问题,round()函数和f-string格式化可能出现非预期的四舍五入结果。例如:round(4.55,1)返回4.5,而round(4.65,1)却返回4.7。类似情况也出现在f-string中,如f"{4.55:.1f}"输出"4.5"。【练一练4.14】执行以下代码,观察输出结果,并分析原因。print(round(1234,-2,round(8459,-1))a=7.05;k=0whilek<10:print(round(a,1),f"{a:.1f}");a+=0.1第4章函数应用4.5.1数学函数4.5

内置函数3.sum函数sum(x,start)sum函数用于对可迭代对象(如列表、元组、集合等)中的元素进行求和操作。语法格式:参数说明:x为可迭代对象,如列表、元组、集合等。可迭代对象的元素应该是可以进行加法运算的数据,通常是整数或浮点数。start为和的初始值,默认为0。如:a=[1,2,3,4],则sum(a,10)的值为20(a元素的和为10,再加上初始值10)。【例4.15】a=[(1,2,3),(4,5)]print(sum(a,()))priint(sum(["good","fine"],"ok"))在“sum(a,())”中,a的元素为元组,sum函数的start参数为空元组,与a的元素数据类型相同,可以求和,print(sum(a,()))的输出为:(1,2,3,4,5)。在“sum(["good","fine"],"ok")”中,Python禁止利用

sum函数对字符串进行拼接,因此会抛出

TypeError

异常。第4章函数应用4.5.1数学函数4.5

内置函数4.max函数和min函数max(iterable,default,key)max函数用于计算多个数据的最大值。语法格式1:语法格式2:max(arg1,arg2,*args,key)参数说明:(1)iterable:为可迭代对象,函数会在这个可迭代对象的元素中找出最大值。(2)default:指定iterable为空时的返回值,若未指定,会引发

ValueError

异常。(3)key:是一个可调用对象,如函数或类,用于指定比较的规则。会对可迭代对象中的每个元素应用

key

,然后根据

key

的返回值来比较大小。缺省时根据元素的数据类型对应的默认方式比较大小。(4)arg1,arg2,*args:至少2个参数,函数会在这些参数中找出最大值。【例4.16】a=[(1,2,3),(4,5),(3,6)]print(max(a))print(max("12","5","30"))print(max(["12","5","30"],key=int))min函数用于计算多个数据的最小值。语法格式与max相同。【练一练4.15】将上例中的max函数替换为min函数,观察运行的输出结果。第4章函数应用4.5.1数学函数4.5

内置函数5.其他数学函数函数作用应用示例abs(x)求实数x的绝对值或求复数x的模。abs(-3)+abs(4-3j)的值为8divmod(x,y)返回元组(x//y,x%y)。divmod(100,7)的值为(14,2)pow(x,y,z)求x**y%z。z缺省时为1。pow(15,3,10)的值为5bin(x)返回整数x的二进制字符串。bin(123)的值为'0b1111011'oct(x)返回整数x的八进制字符串。oct(123)的值为'0o173'hex(x)返回整数x的十六进制字符串。hex(123)的值为'0x7b'chr(x)返回Unicode编码值为x的字符。chr(20013)的值为'中'ord(c)返回字符c的Unicode编码。ord('中')的值为20013【练一练4.16】使用数学函数求解以下问题:(1)分别计算2、3、5、7的算术平方根。(2)判断输入的字符串中是否包含汉字。(3)输入一个大于1000的正整数,输出其二进制、八进制和十六进制数。第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数1.all函数all函数用于判断可迭代对象中的所有元素是否都能转换为逻辑真值True。语法格式:all(iterable)参数说明:iterable为可迭代对象,仅当它的全部元素的逻辑值都为True时返回True,否则返回False。数据为False、0、0.0、None,或空的可迭代对象,逻辑值为False。其他数据的逻辑值均为True。【练一练4.17】输出以下表达式的值,根据输出结果能得出什么结论?(1)all("")(2)all([]) (3)all(()) (4)all({}) 第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数2.any函数any函数用于判断可迭代对象中是否至少有一个元素能转换为逻辑真值True。语法格式:any(iterable)参数说明:可迭代对象iterable存在元素的逻辑值为True时返回True。否则返回False。【练一练4.18】输出以下表达式的值,根据输出结果能得出什么结论?(1)any("") (2)any([]) (3)any(()) (4)any({}) 3.enumerate函数enumerate函数用于将一个可迭代对象组合为一个索引序列,同时列出数据和数据的索引,一般在需要同时获取元素和其对应索引时使用。语法格式:enumerate(interable,start)参数说明:将可迭代对象组合为一个索引序列(enumerate对象,可转换为列表)。start指定索引的起始值(默认值为0)。【例4.17】a=enumerate([15,27,85,12,6])b=list(a)#b=[(0,15),(1,27),(2,85),(3,12),(4,6)]第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数4.sorted函数sorted(interalbe,key,reverse)sorted函数用于对可迭代对象进行排序并返回一个新的已排序列表,原可迭代对象不会被修改。语法格式:参数说明:对可迭代对象iterable排序,返回排序后的列表。参数key指定元素比较大小的规则(函数或类名),reverse指定是否降序,默认值为0,表示升序。【例4.18】a=[23,12,31]a.sort() #直接对a升序排序print(a) #a的元素顺序被改变,输出:[12,23,31] b=[2,6,1,3]c=sorted(b,reverse=1) #对b降序排序print(b) #b并没有改变,输出:[2,6,1,3]print(c) #c是sorted返回的新列表,输出:[6,3,2,1]c=sorted(a,key=lambdax:x%10) #按元素个位数字比较大小,升序排序print(c) #c的值为[31,12,23]第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数5.zip函数zip函数可以将多个可迭代对象中相同位置的元素打包成一个个元组,然后返回由这些元组组成的zip对象,zip函数返回的次对象也是迭代器对象。语法格式:zip(*iterables)参数说明:将多个可迭代对象相同位置的元素打包成元组,返回由这些元组组成的迭代器对象。(1)如果各对象的元素个数不同,则多出的元素会舍弃。如:zip([1,2,3],[4,5],[6,7,8])只包含两个元素:(1,4,6)、(2,5,7)。(2)利用*号操作符可对zip对象解包。如:a=zip([1,2,3],[4,5],[6,7,8]),则zip(*a)是zip对象,包含3个元素:(1,2)、(4,5)、(6,7)。(3)迭代器的主要特点是:不存储数据元素,仅保存迭代算法和当前状态,按需生成元素。其内存占用与数据量无关,但迭代结束后需重新创建才能再次遍历。迭代器本身可迭代,但不支持索引/切片操作。第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数6.filter函数【例4.19】a=[1,2,3];b=[4,5,6];s=[]c=zip(a,b)forx,yinc:s.append(x+y)print(list(c));print(s)执行语句“c=zip(a,b)”后,c为zip对象,包含3个元素:(1,4)、(2,5)、(3,6)。“forx,yinc:s.append(x+y)”等效于“for(x,y)inc:s.append(x+y)”,即(x,y)遍历c,得到s=[5,7,9]。执行“list(c)”时,c需要迭代后才能转换为列表,但c是迭代器,已经在for语句中迭代完成,不能再迭代。因此,list(c)是一个空列表。综上所述,代码的最后输出结果是[][5,7,9]。5.zip函数filter函数用于从可迭代对象中筛选出满足指定条件的元素。语法格式:filter(function,iterable)参数说明:function指定一个函数,iterable为可迭代对象。返回值:返回一个filter对象,也是迭代器对象。该对象包含了iterable中所有使

function指定的函数返回

True

的全部元素。如果参数function指定为None,则包含iterable中所有可转换为True的元素。第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数【例4.20】计算[0,10000]上能被13整除且个位数为1的整数的和。a=range(13,10000,13)b=filter(lambdax:x%10==1,a)s=0forxinb:s+=xc=list(b)print(c,s)a=range(13,10000,13):得到[0,10000]上能被13整除的全部整数。a是range对象,是可迭代对象。b=filter(lambdax:x%10==1,a):从a中筛选出个位数为1的全部整数。b为filter对象。forxinb:s+=x:遍历b,求出b中全部整数的和c=list(b):b需要迭代后才能转换为列表,但b是迭代器,已经在for语句中迭代完成,不能再迭代。因此,list(b)是一个空列表。综上所述,代码运行后的输出结果是:[]387387【练一练4.19】下面代码也能实现上例效果,试比较二者在占用内存上的差异。a=[xforxinrange(13,10000,13)ifx%10==1]print(sum(a))6.filter函数第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数7.map函数map函数用于对可迭代对象中的每个元素应用指定的函数,并返回一个迭代器,该迭代器包含应用函数后的结果。语法格式:map(function,iterable,*iterables)参数说明:(1)function:是要应用到可迭代对象元素上的函数。(2)iterable:是一个可迭代对象。(3)*iterables:可选参数,表示可以传入0个、1个或多个可迭代对象。返回值:返回一个迭代器,该迭代器会生成

function

应用于每个可迭代对象对应元素后的结果。第4章函数应用4.5.2可迭代对象处理函数4.5

内置函数7.map函数【例4.21】a=range(1,10,2)#得到range对象,元素分别为1、3、5、7、9b=range(2,10,3)#得到range对象,元素分别为2、5、8c=map(lambdax,y:x+y,a,b)print(list(c))c=map(lambdax,y:x+y,a,b):当使用map()函数处理两个长度不同的可迭代对象时,结果列表的长度将与较短的那个对象保持一致,所以a后面的两个元素7和9会舍弃。map的function参数为匿名函数lambdax,y:x+y,表示x遍历a,y遍历b。当x为1,y为2,得到c的元素x+y即3,当x为3,y为5,得到c的元素x+y即8,当x为5,y为8,得到c的元素x+y即13,因此,c有3个元素:3、8、13。程序运行后输出:[3,8,13]。【练一练4.20】不运行代码,分析map(lambdax:x**2,range(1,10,2))的返回值。4.6综合应用案例4.6.1案例1:求解汉诺塔问题4.6.2案例2:编写函数计算多个正整数的最大公约数4.6.3案例3:闰年和季节的判断第4章函数应用案例1求解汉诺塔问题4.6综合应用案例问题描述汉诺塔问题是一个只能使用函数递归求解的经典问题:假定有三根石柱,分别标记为A、B、C。在A柱上,从大到小的顺序堆叠有n个圆盘,形成一个塔状结构,最大的圆盘在底部,最小的圆盘在顶部。要求每次只能移动一个圆盘,且不允许将大圆盘放在小圆盘之上。目标是将所有圆盘从A移动到C柱,可以借助B柱作为中间过渡。试编写函数hanoi(n,a,b,c)输出移动过程。其中参数n为a柱上的圆盘数量,b为过渡石柱,c为目标石柱。程序执行效果:输入A柱上的圆盘数量:3移动过程:A-->CA-->BC-->BA-->CB-->AB-->CA-->C案例分析设函数hanoi(n,a,b,c)能够求解汉诺塔问题。则:(1)n=1时,问题可以解决:直接将a柱上的1个圆盘移到c柱上即可。(2)如果n>1,则可按以下方法移动圆盘:调用hanoi(n-1,a,c,b),先将a柱上的n-1个圆盘移到b上;再将a柱上的最后一个圆盘移到c柱。最后,调用hanoi(n-1,b,a,c),将b柱上的n-1个圆盘移到c上。第4章函数应用案例1求解汉诺塔问题4.6综合应用案例参考代码拓展训练因此,汉诺塔问题具备函数递归问题经典模型的全部条件,可以利用函数递归求解。defhanoi(n,a,b,c):ifn==1:print(a,'-->',c,end="")else:hanoi(n-1,a,c,b)#将a柱上面n-1个圆盘移到b柱print(a,'-->',c,end="")#将a柱最后一个圆盘移动到c柱hanoi(n-1,b,a,c)#将b柱的n-1个圆盘移到c柱n=eval(input("输入圆盘数量:"))hanoi(n,'A','B','C')#调用函数输出移动过程修改以上代码,同时输出称动步数。并尝试不断增大n的值,观察n对运行时间的影响。第4章函数应用案例2编写函数计算多个正整数的最大公约数4.6综合应用案例问题描述编写函数gcd_from_ints,计算多个正整数的最大公约数。要求:(1)调用函数时至少需要传递2个正整数。(2)如果调用函数时传递的参数无效,函数返回值为None。(3)可按以下任意方式调用该函数:gcd_from_ints(12,210)gcd_from_ints(a=12,b=210)gcd_from_ints(12,210,36)gcd_from_ints(12,210,x=18,y=40)a=[12,210];gcd_from_ints(*a)a={'a':12,'b':210,'c':40};gcd_from_ints(**a)案例分析

参数类型的确定调用函数时至少需要传递2个正整数,再结合前二种调用方式要求,可以定义2个必需参数。且可以按位置传递,也可按关键字传递,不能设置“/”或“*”参数约束。根据调用方式(3)

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论