第5章
函 数 
函数(function)是执行计算的命名语句序列。将一段代码封装为函数并在需要的位置
进行调用,不仅可以实现代码的重复利用,更重要的是可以保证代码的完全一致。
5.1 函数的定义和使用
1.函数的定义 
函数定义(definition)的语法形式: 
def 函数名([形式参数列表]): #[]表示可选,即一个函数可以没有形式参数 
''' docstring①''' #函数的功能说明,也就是文档字符串 
函数体中的语句 
Python使用关键字def定义函数。关键字def后面有一个空格,然后是函数名,接下来
是一对圆括号,圆括号里面是形式参数(formalparameter),圆括号后面是一个冒号和换行, 
最后是必要的注释以及函数代码。定义函数时需要注意如下几个问题: 
(1)一个函数即使不需要接收任何参数,也必须保留一对圆括号; 
(2)括号后面的冒号必不可少; 
(3)函数体(包括注释部分)相对于def关键字必须向右缩进一定数量的空格。
下面定义一个add()函数,该函数接收两个形式参数x1和x2。 
def add(x1, x2): #函数头 
'''Return the sum of x1 and x2.''' 
return x1 +x2 
在上述定义的add()函数中,其函数体的第1行是注释,也就是文档字符串docstring。
在Python语言中有些工具利用docstring自动生成联机文档,使得用户可以方便地、交互式
地浏览程序代码,如图5-1所示。编写代码时额外添加文档字符串docstring是一种好的编
程实践,请注意培养这样的好习惯。
① docstring代表文档字符串(documentationstring)。
教学课件

第5章 函数 71 
图5-1 函数定义中的文档字符串 
在图5-1中,使用add()函数的_ _doc_ _属性可以查看在该函数的定义中添加的文档字
符串,也可以使用Python的内置函数help()查看add()函数的使用帮助。另外,在调用
add()函数时输入左括号,IDLE等集成开发环境就会立即弹出该函数的使用帮助信息。
2.函数的调用(call) 
函数定义完毕并不能自动运行,只有被调用时才能运行。下面的代码用整数1和2调
用add()函数,该函数的返回值被赋值给变量result。 
result =add(1, 2) 
print(result) 
程序的运行结果: 
3
上述调用add()函数时使用的整数1和2是实际参数(actualparameter),简称实参;而
在函数头使用的参数是形式参数,简称形参。形参没有具体的值,形参的值来自实参。
3.函数的返回值(returnvalue) 
通常,定义一个函数是希望它能够返回一个或多个计算结果,这在Python语言中是通
过关键字return来实现的。无论return语句出现在函数的什么位置,一旦被执行,它都会
立即结束函数的执行过程。如果在函数的定义中没有出现return语句或者执行了不返回
任何值的return语句,Python解释器就认为该函数以returnNone语句结束,即返回一个
空值(None)。下面定义的3个函数demo(x,y),它们的返回值都是None。 
def demo(x, y): 
x +y #没有return 语句
def demo(x, y): 
Return #return 语句不返回任何值
def demo(x, y): 
return None

72 Python程序设计教程(第2版) 
下面定义一个fib()函数,该函数能够输出任意指定范围内的斐波那契(Fibonacci) 
数列①: 
>>>def fib(n): 
'''Print a Fibonacci series up to n.''' 
a, b =0, 1 
while a <n: 
print(a, end=' ') #输出a 的值后,接着输出一个空格 
a, b =b, a+b #元组赋值 
print() #输出一个空行
>>>fib(20) #将20 作为实参调用fib()函数
上述代码的输出结果: 
0 1 1 2 3 5 8 13 
可以将一个函数名赋值给另外一个变量,使得该变量也可以作为函数使用: 
>>>f =fib #这是一种通用的重命名机制
>>>f(20) 
0 1 1 2 3 5 8 13 
由于上述定义的fib()函数中没有return语句,因此Python解释器会自动返回一个
空值。 
>>>print(fib(5)) #注意对比fib(5)与print(fib(5))的输出结果
0 1 1 2 3 
None #fib(5)不输出此行
再看一个例子: 
>>>def demo(): 
pass 
>>>print(demo()) 
None 
修改上述定义的fib()函数,使其返回一个由斐波那契数组成的列表,而不是在该函数
中打印该数列: 
>>>def fib2(n): 
'''Return a list containing the Fibonacci series up to n.''' 
result =[] 
a, b =0, 1 
① 斐波那契数列的前两项为0和1,从第3项开始,每一项都等于前两项之和,如0,1,2,3,5,…。
程序源码

第5章 函数 73 
while a <n: 
result.append(a) 
a, b =b, a+b 
return result 
>>>f100 =fib2(100) #调用fib2()函数
>>>f100 #输出结果
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] 
5.2 函数的参数类型
在函数的定义中,形参列表的一般形式为 
<必选参数>, …, <可选参数>=<默认值>, … 
1.必选参数
没有给出默认值(defaultvalue)的参数都是必选参数。在定义函数时,必选参数必须出
现在可选参数的前面。例如,在下面定义的demo()函数中,参数x就是一个必选参数。在
调用函数时,必须给必选参数赋值。 
def demo(x, y=5): 
pass 
2.可选参数
带有默认值的参数都是可选参数。在定义函数时已经给可选参数指定了默认值。因此
在调用一个函数时,如果没有给可选参数提供值,那么该函数就会使用其默认值。例如,在
上面定义的demo()函数中,y就是可选参数,因此在调用demo()函数时,可以不给可选参
数y提供新值。下面再定义一个函数person()。 
def person(name, gender='male', age=20): 
print('name:', name) 
print('gender:', gender) 
print('age:', age) 
可以使用以下三种方式调用上述定义的person()函数: 
(1)仅给必选参数name赋值,如person('Tim'); 
(2)给必选参数name和可选参数gender赋值,如person('Tim',f'emale'); 
(3)给所有参数赋值,如person('Smith','male',30)。
3.关键字参数
拥有参数名的参数都是关键字参数(这里所说的关键字不是指Python解释器内部定
义并使用的关键字,如if等)。在person()函数的定义中出现的3个参数都是关键字参数。
使用关键字参数可以很方便地给形参赋值,而无须记住各个参数在函数定义中出现的先后
顺序。下面是调用demo()函数的各种方式。

74 Python程序设计教程(第2版) 
demo(3) #正确,x 的取值3,y 的取值5 
demo(x=3) #正确,x 的取值3,y 的取值5 
demo(3, y=4) #正确,x 的取值3,y 的取值4 
demo(y=4, x=3) #正确,x 的取值3,y 的取值4 
demo(y=4, 3) #错误
4.可变长度参数
在Python语言中,可变长度参数有两种类型:一种是用单星操作符“*”定义的;另一
种是用双星操作符“**”定义的。拥有第一种可变长度参数的函数能够接收任意数量的实
参,这些实参被封装成一个元组: 
def multiply(*args): #arg 代表argument(参数) 
z =1 
for arg in args: 
z *=arg 
print(z) 
下面调用multiply()函数。 
multiply(4, 5) #输出结果为20 
multiply(10, 9) #输出结果为90 
multiply(2, 3, 4) #输出结果为24 
在可变长度参数的前面,可能会出现必选参数: 
def demo(name, *args): #可变长度参数args 前有一个必选参数name 
print(name, end=', ') 
for arg in args: 
print(arg, end=' ') 
demo('Jim', 'nice', 'to', 'meet', 'you!') 
在上一行的函数调用中,name的取值为Jim,args的值为元组(n'ice',t'o','meet',y' ou! ')。
程序的输出结果如下。 
Jim, nice to meet you! 
再看一个例子: 
>>>def concat(*args, sep="/"): 
return sep.join(args) 
上述代码中的参数sep为可选参数,也是关键字参数。 
>>>concat("a", "b", "c") 
'a/b/c'

第5章 函数 75 
在上述代码中,可选参数sep 取默认值“/”,可变长度参数args的值为元组("a", 
"b","c")。 
>>>concat("one", "two", "three", sep=".") 
'one.two.three' 
在上一行的函数调用中,可变长度参数args的值为元组("one","two","three"),并
且为可选参数sep指定了新值“.”。
如果在函数形参列表的最后有一个用双星操作符“**”定义的可变长度参数,那么该
函数能够接收任意数量的实参,而且这些实参被封装成一个字典: 
def print_values(**kwargs): 
for key, value in kwargs.items(): 
print("The value of {} is {}".format(key, value)) 
print_values(my_name="Tom", your_name="Tim") 
上述代码的执行结果: 
The value of my_name is Tom 
The value of your_name is Tim 
注意:在函数的定义中,双星操作符“**”必须出现在单星操作符“*”的后面。
下面定义一个demo()函数。 
def demo(*args, **kwargs): #**kwargs18 必须出现在*args 的后面 
for arg in args: 
print(arg, end=' ') 
print() #输出一个回车换行 
for key, value in kwargs.items(): 
print(key, '=>', value) 
下面调用demo()函数。 
demo('hello', 'world', name='Tom', age=30) 
程序的输出结果如下。 
hello world 
name =>Tom 
age =>30 
上述四种参数类型相互之间并不是互斥的关系,如可选参数同时也是关键字参数。
5.函数参数的赋值方式
为方便读者阅读,再次给出person()函数的定义: 
程序源码

76 Python程序设计教程(第2版) 
def person(name, gender='male', age=20): 
print('name:', name) 
print('gender:', gender) 
print('age:', age) 
在调用函数时,如果不使用参数名给形参赋值,那么将按照实参出现的顺序依次给对应
位置上的形参赋值,这种方式叫作按位置赋值。如下面的函数调用。 
person('Tim') 
执行上述代码后,形参name得到的值为Tim,gender和age取默认值,也就是gender 
的取值为male,age的取值为20。再看下面几种函数调用。 
person('Tim', 'female') #name 为Tim,gender 为female,age 取默认值20 
person('Smith', 'male', 30) #name 为Smith,gender 为male,age 为30 
再强调一次:必须给必选参数指定参数值。综上可知:按位置给形参赋值时,实参的
出现顺序非常重要。除了按位置给形参赋值外,还可以通过指定参数名的方式给形参赋值, 
这种方式叫作按关键字赋值。如下面的函数调用。 
person(name='Tim', gender='female') 
person(gender='female', name='Tim') 
person(age=22, gender='female', name='Tim') 
很显然,按关键字赋值的优点是不用考虑实参出现的先后顺序。
也可以将这两种赋值方式混合使用: 
person('Sue', gender='female') 
实参Sue按位置给形参name赋值,而实参female按关键字给gender赋值。
注意:当混合使用这两种赋值方式时,一定要保证按位置赋值出现在按关键字赋值的
前面。下面的函数调用是错误的。 
person(gender='female', 'Sue') 
在上述的函数调用中,按位置赋值应出现在前面,即person(S' ue',gender=f'emale')。
再看两个错误的函数调用方法: 
person('Gorge', name='Gorge') #为同一个参数指定重复的值
person(weight=150) #使用未知的形参weight 
有时只能采用按关键字赋值的方式给形参赋值,如下面定义的函数。 
def demo(*args, name): #可变长度参数args 后面有一个必选参数name 
print(name, end=', ')

第5章 函数 77 
for arg in args: 
print(arg, end=' ') 
假如按位置赋值,也就是采用下面的方式调用demo()函数,读者可以试一试能否得到
期望的输出结果。 
demo('nice', 'to', 'meet', 'you!', 'Jim') 
此时应该按关键字赋值: 
demo('nice', 'to', 'meet', 'you!', name='Jim') 
5.3 参数解包
有时实参已存储在列表、元组等数据容器中,但是函数调用却需要单独的位置参数,这
时就需要使用单星操作符“*”将实参从这些数据容器中解包(unpacking)出来: 
>>>def demo(x, y, z): 
print(x+y+z) 
>>>lt =[1, 2, 3] #列表lt 
>>>demo(*lt) 
6
>>>tu =(1, 2, 3) #元组tu 
>>>demo(*tu) 
6
>>>st ={1, 2, 3} #集合st 
>>>demo(*st) 
6
实参为字典时: 
>>>dt ={1:'a', 2:'b', 3:'c'} 
>>>demo(*dt) #默认使用字典的键
6
>>>demo(*dt.values()) #明确指定使用字典的值
abc 
>>>list(range(3, 6)) 
[3, 4, 5] 
>>>args =[3, 6] 
>>>list(range(*args)) #解包列表,得到实参3 和6 
[3, 4, 5] #执行结果相同
类似地,字典可以使用双星操作符“**”进行解包以便传递关键字参数:

78 Python程序设计教程(第2版) 
>>>def person(name, gender='male', age=20): 
print('name:', name) 
print('gender:', gender) 
print('age:', age) 
>>>dt ={"name": "Tom", "gender": "male", "age": 40} 
>>>person(**dt) 
name: Tom 
gender: male 
age: 40 
5.4 递归函数
简单地说,算法是解决问题的方法与步骤。递归算法(recursivealgorithm)的核心思想
是分治策略。分治,是“分而治之”(divideandconquer)的意思。分治策略将一个复杂问题
反复分解为两个或更多个相同的,或相似的子问题,直到这些子问题可以直接求解,最后将
子问题的解合并起来,就能得到原问题的解,如图5-2所示。
图5-2 分治策略
在Python语言中,分治策略是通过递归函数实现的。一个函数在其函数体内调用它
自身,这种函数叫作递归函数。递归函数由终止条件和递归条件两部分构成。下面定义一
个计算阶乘的函数factorial(n)。 
def factorial(n): #factorial 是阶乘的意思
if n <=1: #终止条件
return 1 
else: 
return n * factorial(n -1) #递归条件
print(factorial(5))

第5章 函数 79 
上述代码的输出结果: 
120 
调用上述定义的factorial(n)函数计算5的阶乘,执行过程如下所示。 
factorial(5) = 5 * factorial(4) 
= 5 * 4 * factorial(3) 
= 5 * 4 * 3 * factorial(2) 
= 5 * 4 * 3 * 2 * factorial(1) 
= 5 * 4 * 3 * 2 * 1 
= 120 
定义计算斐波那契数列的递归函数fib(n): 
def fib(n): 
""" 计算斐波那契数列,参数n 为数列的第n 项""" 
if n in [0, 1]: 
return n 
else: 
return fib(n-1) +fib(n-2) 
for i in range(10): 
print(fib(i), end=" ") 
上述代码的执行结果: 
0 1 1 2 3 5 8 13 21 34 
5.5 lambda 函数
可以使用lambda关键字创建小型匿名函数,如: 
lambda x, y: x +y 
该函数返回两个参数之和,可以像普通函数那样使用lambda函数。注意,lambda函数
在形式上只能是一个表达式。 
>>>add_one =lambda x: x +1 
>>>add_one(1) 
2
用普通函数实现上述匿名函数的功能: 
def add_one(x): 
return x +1