
1.7.1 函数
在实际开发中,把可能需要反复执行的代码封装为函数,然后在需要执行该段代码功能时调用封装好的函数,这样不仅可以实现代码的复用,更重要的是可以保证代码的一致性,只需要修改该函数代码便可使所有调用位置均得到体现。同时,把大任务拆分成多个函数也是分治法和模块化设计的基本思路,这样有利于将复杂问题简单化。
1.基本语法
关于函数的基本语法如下:
def 函数名([参数列表]): ''' 注释 ''' 函数体
其中,def是用来定义函数的关键字。定义函数时在语法上需要注意的主要问题如下。
1)不需要说明形参类型,Python解释器会根据实参的值自动推断形参类型。
2)不需要指定函数返回值类型,这由函数中return语句返回的值来确定。
3)即使该函数不需要接收任何参数,也必须保留一对空的圆括号。
4)函数头部括号后面的冒号必不可少。
5)函数体相对于def关键字必须保持一定的空格缩进。
下面举一个简单的Python函数定义的例子,它将一个字符串作为传入参数,再打印到标准显示设备上。
【例1-55】定义一个简单的Python函数
def printme( str ): "打印传入的字符串到标准显示设备上" print str return
【例1-56】编写函数,计算并输出斐波那契数列中小于参数n的所有值,并调用该函数。
def fib(n): a,b = 1,1 while a < n: print(a,end='') a,b = b,a+b fib(1000)
2.函数嵌套
在Python中,函数的用法多种多样,但我们平时使用的大多数是一些基本算法,对于那些复杂的函数,嵌套必不可少。所谓函数嵌套,就是指在函数中定义的函数。函数嵌套保证了代码的模块化、复用性和可靠性。
【例1-57】函数嵌套
def fun1(): m=3 def fun2(): n=4 print(m+n) fun2() fun1()
在上述程序运行后,编译器会将函数fun1()的函数体存放到内存中,先不去执行,直到程序的最后一行,这时发现函数fun1()被调用,于是运行函数,令m=3,然后发现了新定义的函数fun2(),于是将fun2()放在内存中,在后续调用时,再去运行其中的赋值及打印操作。需要注意,函数内定义的函数只能在函数内调用,就像函数内定义的变量,无法在外面调用。
3.lambda函数
lambda函数又被称为匿名函数,它没有复杂的函数定义,仅由一行代码构成。
lambda函数的语法如下:
result = lambda arg1,arg2,arg3,...,argN:expression
其中,result用于接收lambda函数的结果,arg1,arg2,arg3,....,argN:指的是可选参数,用于指定要传递的参数列表,参数间使用“,”分隔。expression为必选参数,它是一个表达式,用于描述函数的功能。如果函数有参数,那么将在这个表达式中使用。
【例1-58】lambda函数的使用示例
>>> lambda x, y : x+y
x和y是函数的两个参数,冒号后面的表达式是函数的返回值,你能一眼看出这个函数是在求两个变量的和,但作为一个函数,没有名字如何使用呢?根据例1-59中所示的调用方式,我们给这个匿名函数绑定一个名字,使调用匿名函数成为可能。
【例1-59】result应用
add= lambda x, y:x+y result = add(1,2) print(result)
需要注意的是,在使用lambda函数时,参数可以有多个,但表达式只能有一个。而且在表达式中不能出现if、while这种非表达式语句。lambda函数使用起来很方便。
4.递归函数
如果在一个函数中直接或间接地调用了该函数自身,那么便称这个过程为递归调用。函数的递归调用是函数调用的一种特殊情况,函数调用自己,自己再调用自己,如此反复调用至某个条件得到满足,此时便不再调用,最后再一层一层地返回,直到该函数的第一次调用。
递归函数的调用过程如图1-39所示。
图1-39 递归函数的调用
需要注意的是,递归函数必须有一个明确的结束条件。每当进入更深一层的递归时,问题的规模相对于上一次递归都应减少,而且相邻两次调用要有紧密的联系,通常前一次的输出是后一次的输入。如下所示的便是一个简单的递归计算示例。
【例1-60】使用递归计算5的阶乘
def fact(n): If n == 1: return 1 return n*fact(n-1) print(fact(5))
上面的程序运行结果为120,在这个过程中,fact()函数调用了自身return n*fact(n-1),重复的调用,最后直到n等于0,递归结束。
5.函数参数
定义函数时圆括弧内是使用逗号分隔开的形参列表,函数可以有多个参数,也可以没有参数,但定义和调用函数时一对圆括弧必须要有,表示这是一个函数并且不接受参数。调用函数时向其传递实参,将实参的引用传递给形参的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么值就够了,函数内部的复杂逻辑被封装起来,调用者无须了解。
6.位置参数
实参位置和形参保持一致,按形参声明的先后顺序一一赋值。
7.关键字参数
关键字参数主要指调用函数时的参数传递方式,调用函数的时候以形参=实参的方式来传参,此时实参的顺序无所谓。这样可以避免用户需要牢记参数位置和顺序的麻烦,使得函数的调用和参数的传递更加灵活。
关于调用函数时的关键字参数的用法示例如下所示。
【例1-61】编写关键字参数
def func1(a, b, c): print(a, b, c) func1(10, 20, 30) # 通过位置参数传参 func1(c=30, a=10, b=20) # 通过关键字参数传参 # 通过位置参数和关键字参数结合传参(注意关键字参数必须在位置参数的后面) func1(10, 20, c=30)
8.默认值参数
声明函数的时候,可以给参数赋默认值。如果一个形参有默认值了,那么调用函数时这个参数就可以不用传参。如果有的形参有默认值,有的形参没有默认值,那么有默认值的形参要放在没有默认值的形参之后。值得注意的是,调用函数要保证每个参数都有值。
【例1-62】编写默认值参数
# 参数c有默认值,调用函数时c不必传参 def func2(a, b, c=0): print(a, b, c) # a=100, b=200 func2(100, 200, 300) func2(100, 200) func2(a=100, c=200, b=150) func2(b=110, a=220)
9.可变长度参数
声明函数的时候,在参数名前加*,可以用来同时获取多个实参的值。实质是将带*的参数变成元组,将多个实参的值作为元组的元素。
注意 如果函数中既有可变长度参数又有普通参数,那么可变长度参数必须放在普通参数后边。
【例1-63】编写可变长度参数
def yt_sum(*nums): print(nums) yt_sum() yt_sum(1) yt_sum(1, 2) yt_sum(1, 2, 3) yt_sum(1, 2, 3, 4, 5) def func5(name, age, *scores): print(name, scores) func5('夏明', 18, 209) func5('小花', 10, 20, 30)