Python入门(七):函数
函数
函数是一段具有特定功能的、可重用的语句组,通过函数名来表示和调用。
函数包括两部分:函数的定义和函数的使用。
函数主要有两个作用:降低编程难度和增加代码复用。函数是一种功能抽象,利用它可以将一个大问题拆分成若干个小问题,分而治之,为每个小问题编写程序,通过函数封装,使得大问题得以解决;函数可以在一个程序的多个位置使用,也可以用于多个程序,当代码需要修改时,只需在函数中修改一次就可使得所用调用位置的功能得以改变,这种代码复用降低了代码维护难度。
函数定义
有时候并没有现成的函数满足我们的需求,这时候就需要自己定义函数
定义函数需使用 def 语句,依次写出函数名、括号、括号中的参数和冒号:,然后在代码块中编写函数体,函数的返回值用 return 语句表示。
def <函数名>(<参数列表>):
<函数体>
return <返回值列表>
其中,所指定的参数是一种占位符,且函数若不经过调用,则不会被执行。
当函数没有 return 时,仅表示执行一段代码功能。
IPO:参数(Input) 函数体(Process) 结果(Output)
例:定义一个 square_of_sum 函数,它接受一个list,返回list中每个元素平方的和。
def square_of_sum(L):
sum = 0
for x in L:
sum = sum + x*x
return sum
函数使用
函数使用需要了解该函数的名称及参数设置,内置函数可查阅相关文档,此处为官方文档 http://docs.python.org/2/library/functions.html#abs ,还可以输入命令help(函数名)
查看帮助。此外,可通过type(函数名)
查看函数类型。如下图所示:
例:sum()函数接受一个list作为参数,并返回list所有元素之和。请计算 11 + 22 + 33 + … + 100100。
L = [ ]
x = 1
while x <= 100:
L.append(x*x)
x += 1
print(sum(L))
函数可以返回多个值,但实质上返回的是一个元组类型的数据集合
例:请编写一个函数,返回一元二次方程 ax² + bx + c = 0 的两个解。
import math
def quadratic_equation(a, b, c):
t = math.sqrt(b**2-4*a*c)
return (-b+t)/(2*a),(-b-t)/(2*a)
print(quadratic_equation(2, 3, 0))返回
函数也可以有多个返回语句,用于不同情况下结果的输出。
def m(x):
try:
if x>0:
return x+1
else:
return x-1
except:
return 0
另外,Python 语言最小函数可以不表达任何功能,如下:
def f():
pass
其中,保留字 pass 表示不进行任何操作,仅起到占位的作用,因为函数体内部总要编写一段代码。对 f() 的调用不实现任何功能。
具体来说,函数使用共分为4个步骤:
- 函数定义
- 函数调用
- 函数执行
- 函数返回
需要注意的是,函数定义不一定放在调用之前,如下图所示:
编程中大量使用函数已经成为一种编程范式,叫做函数式编程。函数式编程的主要思想是把程序过程尽量写成一系列函数调用,这能够使代码编写更简洁、更易于理解,是中小规模软件项目中最常用的编程方式。
函数参数传递
函数的参数在定义时可以指定默认值,当函数被调用时,如果没有传入对应的参数值,则用默认值替代,函数定义时的具体语法形式如下:
def <函数名>(<非可选参数列表>,<可选参数> = <默认值>):
<函数体>
return <返回值列表>
定义默认参数
函数的默认参数的作用是简化调用,只需传入必需参数即可调用函数。但是在需要的时候,又可以传入额外的参数来覆盖默认参数值。
由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面。
例:请定义一个 greet() 函数,它包含一个默认参数,如果没有传入,打印 ‘Hello, world.’,如果传入,打印 ‘Hello, xxx.’
def greet(name='world'):
print ('hello,'+ name +'.')
greet()
greet('Bart')
例:计算 n!//m 的值。
def fact(n,m=2):
s = 1
for i in range(1,n+1):
s *= i
return s//m
fact(5) #60
fact(5,4) #30
定义可变参数
如果想让一个函数能接受任意个参数,我们就可以定义一个可变参数:
Python解释器会把传入的一组参数组装成一个元组传递给可变参数,因此,在函数内部,直接把变量看成一个 tuple 就好了。
def fact(n,*b):
s = 1
for i in range(1,n+1):
s *= i
for item in b:
s *= item
return s
fact(3,9) #3!×9
fact(3,9,2,1) #3!×9×2×1
例:请编写接受可变参数的 average() 函数。
def average(*y):
sum = 0.0
if len(y) == 0:
return sum
for x in y:
sum = sum + x
return sum/len(y)
print(average(1, 2, 2, 3, 4))
参数传递方式
在函数中,参数传递方式有两种:位置传递和名称传递。函数调用时,默认采用按照位置顺序的方式传递给函数,即位置传递。若想使用名称传递,则指定参数的值即可,格式如下:
# 名称传递
<函数名>(<参数名> = <实际值>)
def fact(n,m):
s = 1
for i in range(1,n+1):
s *= i
return s//m
fact(9,3) #位置传递
fact(m=3,n=9) #名称传递
变量作用域
根据程序中变量所在的位置和作用范围,变量分为局部变量和全局变量。
局部变量
局部变量指在函数内部定义的变量,仅在函数内部有效,当函数退出时变量不再存在。
全局变量
全局变量指在函数之外定义的变量,在程序执行全过程有效。全局变量在函数内部使用时,需要提前使用保留字 global 声明。
使用规则
模块化设计
程序由一系列代码组成,如果代码是顺序但无组织的,不仅可读性差,而且维护和升级的难度大。解决这个问题最好的方法,就是如日本马拉松某名将所说,将路程(程序)分成若干个小路程(小程序),每个程序段完成一部分特定的功能。使用函数对程序合理划分为功能模块,并基于模块设计程序是一种常用方法,称为“模块化设计”。
模块化设计指通过函数的封装功能将程序划分成主程序、子程序和子程间关系的表达。模块化设计是使用函数设计程序的思考方法,以功能块为基本单位,一般有两个基本要求:
- 紧耦合:尽可能合理划分功能块,功能块内部耦合度高
- 松耦合:模块间关系尽可能简单,功能块之间耦合度低
耦合性指程序结构中各模块之间相互关联的程度,它取决于各模块间接口的复杂程度和调用方式。耦合性是影响软件复杂程度和设计质量的一个重要因素。
紧耦合指模块或系统间关系紧密,存在较多和复杂的相互调用,松耦合相反。紧耦合的缺点在于更新一个模块可能导致其他模块变化,代码复用较困难。松耦合一般基于消息或协议实现,系统间交互简单。松耦合代表了模块化,从系统观点来看,松耦合是总体设计原则。
需要注意的是,使用函数只是模块化设计的必要非充分条件,根据计算需求合理划分即可。一般来说,完成特定功能或经常复用的语句组应用函数封装,并尽可能减少函数间参数和返回值的数量。
lambda函数
lambda 函数是一种匿名函数,使用 lambda 保留字定义,函数名即是返回结果,通常用于定义简单的,能在一行内表示的函数。此外 lambda 函数主要用作一些特定函数或方法的参数,有一些固定使用方式。
函数递归
同数学中的定义相同,函数递归就是函数定义中调用函数本身的方式。
如果一个函数在内部调用自身,这个函数就是递归函数。
在函数递归中,有两个关键特征:链条 和 基例。
同数学中的数学归纳法的原理相同,递归是数学归纳法思维的编程体现。
递归函数定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
例1:计算阶乘 n! = 1 * 2 * 3 * … * n
def fact(n):
if n == 1:
return 1
else:
return n*fact(n-1)
例2:将下列式子用 Python 程序表示。
def fact(n):
if n==0:
return 1
else:
return n*fact(n-1)
例3:字符串反转
除了我们在前文字符串操作中提到的一个直接命令[::-1]
外,我们可以用函数来实现
def rvs(s):
if s == "":
return s
else:
return rvs(s[1:])+s[0]
例4:斐波那契数列
def f(n):
if n==1 or n==2:
return 1
else:
return f(n-1)+f(n-2)
例5:汉诺塔问题
count = 0
def hanoi(n,src,dst,mid): #src:原始位置 dst:目标位置 mid:中间位置
global count
if n == 1:
print("{}:{}->{}".format(n,src,dst))
count += 1
else:
hanoi(n-1,src,mid,dst)
print("{}:{}->{}".format(n,src,dst))
count += 1
hanoi(n-1,mid,dst,src)
hanoi(3,"A","C","B")
print(count)