函数是一段可以重复多次调用的代码,通过输入的参数值,返回需要的结果。
一、函数的定义
函数的定义使用保留字def定义。函数在使用前必须定义,函数的类型即返回值的类型。
Python函数定义的格式如下:
def 函数名 (参数1[=默认值1],参数2[=默认值2]...): ... return 表达式
函数名可以是字母、数字或下划线组成的字符串,但不能以数字开头。函数的参数放在一对圆括号中,参数的个数可以有一个或多个,参数之间用逗号隔开,这种参数称之为形式参数。
例子:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] #函数的定义 from __future__ import division def arithmetic(x,y,operator): result = { "+":x + y, "-":x - y, "*":x * y, "/":x / y }
arithmetic()已经创建成功,剩下的就是函数的调用的问题了。函数可以在当前文件中调用,也可以在其他模块中调用。
函数调用的格式如下所示:
函数名(实参1,实参2...)
函数的调用采用函数名加一对圆括号的方式,圆括号内的参数是传递给函数的具体值。函数调用中的实参列表分别与函数定义中的形参列表对应。
下图说明了实际参数和形式参数的对应关系。
arichmetic()的调用如下所示:
#函数的调用 print arithmetic(1,2,"+")
注意:
实际参数必须与形式参数一一对应,参数的顺序和参数的类型必须一致,否则将出现错误计算。如果参数提供默认值,顺序可以不一致。
二、函数的参数
在Python中的任何变量都是对象,所以参数只支持引用传递的方式。Python通过名字绑定的机制,把实际参数的值和形式参数的名称绑定在一起。即把形式参数传递到函数所在的局部命名空间中,形式参数和实际参数指向内存中的同一个存储空间。
函数的参数支持默认值。当某个参数没有传递实际的值时,函数将使用默认参数计算。
例如可以给arichmetic()参数都提供一个默认值。
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def arithmetic(x = 1,y = 1,operator = "+"): result = { "+":x + y, "-":x - y, "*":x * y, "/":x / y } return result.get(operator) print arithmetic(1,2) print arithmetic(1,2,"=") print arithmetic(y=3,operator = "-") print arithmetic(x=4,operator = "-") print arithmetic(y=3,x=4,operator = "-")
输出结果:
---------- python2.7 ---------- 3 None -2 3 1 输出完成 (耗时 0 秒) - 正常终止
参数可以是变量,也可以是元组、列表等内置数据结构:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def arithmetic(args = [],operator = "+"): x = args[0] y = args[1] result = { "+":x + y, "-":x - y, "*":x * y, "/":x / y } print arithmetic([1,2])
输出结果:
---------- python2.7 ---------- None 输出完成 (耗时 0 秒) - 正常终止
由于参数实现了名字绑定的机制,所以在使用默认参数时,可能会出现预期之外的结果:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def append(args = []): args.append(0) print args append() append([1]) append()
输出结果:
---------- python2.7 ---------- [0] [1,0] [0,0] 输出完成 (耗时 0 秒) - 正常终止
为了避免这个问题,可以在append()中添加一个条件判断语句。如果列表中args中没有任何元素,则先把args列表置空,谈后再添加元素:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def append(args = []): if len(args) <=0: args = [] args.append(0) print args append() append([1]) append()
输出结果:
---------- python2.7 ---------- [0] [1, 0] [0] 输出完成 (耗时 0 秒) - 正常终止
在程序的开发过程中,常常需要传递可变长度的参数。在函数的参数前使用标识符“*”可以实现这个要求。“*”可以引用元组,把多个参数组合到一个元组中。
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(* args): print args func(1,2,3)
输出结果:
---------- python2.7 ---------- (1, 2, 3) 输出完成 (耗时 0 秒) - 正常终止
三、函数的返回值
函数的返回值使用return语句,return后面可以是变量或表达式。
例子:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] from __future__ import division def arithmetic(x,y,operator): result = { "+":x + y, "-":x - y, "*":x * y, "/":x / y } return result.get(operator)
Python中即使函数没有返回值,依然可以获得返回值。
例子:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(): pass print func()
输出结果:
---------- python2.7 ---------- None 输出完成 (耗时 0 秒) - 正常终止
None是Python中的一个对象,不属于数字也不属于字符串。当函数中的return语句不带任何参数时,返回的结果也是None
例子:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(): return print func()
输出结果:
---------- python2.7 ---------- None 输出完成 (耗时 0 秒) - 正常终止
如果需要返回多个值,可以把这些值打包到元组中。在调用时,对返回的原则解包即可。
例子:输入0、1、2,返回2、1、0
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(x,y,z): l = [x,y,z] l.reverse() numbers = tuple[1] return numbers x,y,z = func(0,1,2) print x,y,z
第二种解决方法:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(x,y,z): l = [x,y,z] l.reverse() a,b,c = tuple[1] return a,b,c x,y,z = func(0,1,2) print x,y,z
在一个函数中也可以使用多个return语句。
例如if…else语句的各个分支最后那个,返回不同的结果:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(x): if x > 0: return "x > 0" elif x == 0: return "x = 0" else: return "x < 0" print func(-2)
输出结果:
---------- python2.7 ---------- x < 0 输出完成 (耗时 0 秒) - 正常终止
注意:
不推荐在一个函数中使用富哦个return语句,return语句过多往往会造成程序的复杂,这时就需要对代码进行重构了。
如果函数中有多个return语句,可以通过增加一个变量的方法来减少return语句:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(x): if x > 0: result = "x > 0" elif x == 0: result = "x = 0" else: result = "x < 0" return result print func(-2)
输出结果:
---------- python2.7 ---------- x < 0 输出完成 (耗时 0 秒) - 正常终止
四、函数的嵌套
函数的嵌套是指在函数的内部调用其他函数。Python不仅支持函数体内的嵌套,还支持函数定义的嵌套。
例如:
计算表达式(x+y)*(m-n)的值。
可以把计算步骤分为3步:先计算表达式x+y,其次计算表达式m-n,最后计算两个结果的乘积。
因此,可以设计3个函数。
第一个函数sum()计算x+y的值;
第二个函数sub()计算m-n的值;
第三个函数计算前面两者的乘积。
例子:函数之间的调用操作
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def sum(a,b): return a + b def sub(a,b): return a - b def func(): x = 1 y = 2 m = 3 n = 4 return sum(x,y)*sub(m,n) print func()
输出结果:
---------- python2.7 ---------- -3 输出完成 (耗时 0 秒) - 正常终止
注意:
函数嵌套的层数不宜过多,否则容易造成代码的可读性差、不易维护等问题。一般函数的嵌套调用控制在3层以内为好。
上面的代码可以换一种形式实现,即把函数sum()、sub()放到func()的内部,如下图所示:
例子:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(): x = 1 y = 2 m = 3 n = 4 def sum(a,b): return a + b def sub(a,b): return a - b return sum(x,y)*sub(m,n) print func()
输出结果:
---------- python2.7 ---------- -3 输出完成 (耗时 0 秒) - 正常终止
内部函数sum()、sub()也可以直接使用外部函数func()定义的变量,如下图:
例子:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(): x = 1 y = 2 m = 3 n = 4 def sum(): return x + y def sub(): return m - n return sum()*sub() print func()
输出结果:
---------- python2.7 ---------- -3 输出完成 (耗时 0 秒) - 正常终止
说明:
尽量不要在函数内部定义函数。这种方式不便于程序的维护,容易造成逻辑上的混乱。
五、递归函数
递归函数可以在函数主题内直接或者间接地调用自己,即函数的嵌套是函数本身。递归四一种程序设计方法,使用递归可以减少重复的代码,使得程序变得简洁。
递归的过程分为两个阶段:递推和回归。
递归函数的原理如下:
第一阶段,递归函数在内部调用自己。每一次函数调用又重新开始执行此函数的代码,直到某一级递归程序结束。
第二阶段,递归函数从后往前返回。递归函数从最后一级开始返回,直到返回到第一次调用的函数体内。即递归逐级调用完毕后,再按照相反的顺序逐级返回。
特别说明:
递归函数需要编写递归结束的条件,否则递归程序将无法结束。一般通过判断语句来结束程序。
计算阶乘是一个经典的递归实现。
例如,计算5!的结果。在设计程序时,可以根据n是否等于1进行判断。每次递归调用,传入参数n-1.直到n=1时,返回1!等于1.再依次返回2!、3!、4!、5!的计算结果。如下图所示:
例子:用递归函数实现阶乘的计算过程
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] #计算阶乘 def refunc(n): i = 1 if n > 1: i = n n = n * refunc(n - 1) print "%d! =" %i,n return n refunc(5)
输出结果:
---------- python2.7 ---------- 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 输出完成 (耗时 0 秒) - 正常终止
特别注意:
每次调用递归函数都会复制函数中所有的变量,再执行递归函数。程序需要较多的存储空间,这对程序的性能会有一定的影响。因此,对于没有必要进行递归的程序,最好用其他的方法进行改进。
可以使用reduce()快速实现阶乘的运算:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] #计算reduce阶乘 print "5! =",reduce(lambda x,y:x * y,range(1,6))
输出结果:
---------- python2.7 ---------- 5! = 120 输出完成 (耗时 0 秒) - 正常终止
六、lambda函数
lambda函数用于创建一个匿名函数,函数名未和标识符进行绑定。使用lambda函数可以返回一些简单的运算结果。
lambda函数的格式如下所示:
lambda 变量1,变量2...:表达式
其中,变量列表用于表达式的计算。lambda属于函数,因此变量列表后需要一个冒号。通常把lambda赋值给一个变量,变量就可以作为函数使用。
例如:
#赋值 func = lambda 变量1,变量2...:表达式 #调用 func()
这样就把lambda和变量func绑定在一起了,变量func的名字就是函数名。
例子:使用lambda函数来计算(x + y)*(m - n)
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] def func(): x = 1 y = 2 m = 3 n = 4 sum = lambda x,y:x + y print sum sub = lambda m,n:m - n print sub return sum(x,y) * sub (m,n) print func()
输出结果:
---------- python2.7 ---------- <function <lambda> at 0x00000000022529E8> <function <lambda> at 0x0000000002252A58> -3 输出完成 (耗时 0 秒) - 正常终止
注意:
lambda也称之为表达式。lambda中只能使用表达式,不能使用判断、循环等多重语句。
例子:把lambda赋值给一个变量使用,也可以把lambda直接作为函数使用:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] #lambda的函数用法 print(lambda x:-x)(-2)
输出结果:
---------- python2.7 ---------- 2 输出完成 (耗时 0 秒) - 正常终止
说明:
定义匿名函数lambda x:-x,用于返回数字的绝对值。
七、generator函数
生成器(generator)的作用是一次产生一个数据项,并把数据项输出。genera函数可以用在for循环中遍历。generator函数所具有的每次返回一个数据项的功能,使得迭代器的性能更佳。
generator函数的定义如下所示:
def 函数名(参数列表): ... yield 表达式
generator函数的定义和普通函数的定义没什么区别,只要在函数体内使用yield生成数据项即可。
generator函数可以被for循环遍历,而且可以通过next()方法获得yield生成的数据项。
例子:
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] #定义generator函数 def func(n): for i in range(n): yield i #在for循环中输出 for i in func(3): print i #使用next()输出 r = func(3) print r.next() print r.next() print r.next() print r.next()
输出结果:
---------- python2.7 ---------- 0 1 2 0 1 2 Traceback (most recent call last): File "Noname1.py", line 17, in <module> print r.next() StopIteration 输出完成 (耗时 0 秒) - 正常终止
说明:
参数n表示一个数字,该函数依次生成0到n个数字。
最后一次调用r.next(),由于n的值等于3,因此生成3个数字后,已经没有数据可生成。Python解释器抛出异常StopIteration
特别说明:
yield保留字与return语句的返回值和执行原理都不相同。yield生成值并不会终止程序的执行,返回值后程序继续往后执行。return返回值后,程序将中止执行。
例子:yield和return语句的区别。
#!/usr/bin/env python # -*- coding=utf-8 -*- #Using GPL v2.7 #Author: [email protected] #yield与return区别 def func(n): for i in range(n): return i #直接返回i的值,循环语句将终止,整个程序到此结束 def func2(n): for i in range(n): yield i #循环生成n个数字,循环语句不会被终止 print func(3) f = func2(3) #yield并没有返回任何值,而是返回了函数func2()的地址 print f print f.next() print f.next()
输出结果:
---------- python2.7 ---------- 0 <generator object func2 at 0x000000000215A6C0> 0 1 输出完成 (耗时 0 秒) - 正常终止
generator函数可以返回元素的值,而序列也可以获取元素的值。但是两者还是存在很大的区别。generator函数依次只返回一个数据项,占用更少的内存。每次生成数据都要记录当前的状态,便于下一次生成数据。数据的访问时通过next()方法实现的。当访问越界时,generator函数会抛出异常StopIteration。序列一次返回所有的数据,元素的访问时通过索引完成的。当访问越界时,序列提示list index out of range错误。
当程序需要较高的性能或一次只需要一个值进行处理时,使用generator函数。当需要获取一次性一组元素的值时,使用序列。