本文共 6346 字,大约阅读时间需要 21 分钟。
本篇文章我们来说说Python中的函数,那么什么是函数呢?
函数是Python为了代码最大程度地重用和最小化代码冗余而提供的基本程序结构。
函数是一种设计工具,能让程序员将复杂的系统分解为可管理的部件,在需要时能够按需去调用这些部件。
函数用于将相关功能打包并参数化
在Python中可以创建4种函数:
全局函数:定义在模块中
局部函数:嵌套于其它函数中
lambda(匿名)函数:仅是一个表达式,可以出现在任何位置,使用的灵活度比函数强得多。它生成一个函数对象,若想多次调用,则应将其赋予某变量名进行调用
方法:与特定数据类型关联的函数,并且只能与数据类型关联一起使用。说白了就是定义在类(class)中的函数
创建函数,语法如下:
1 2 | def functionName(parameters): suite |
def是一个可执行语句,因此可以出现在任何能够使用语句的地方,甚至可以嵌套于其它语句,例如if或while中。
def创建了一个对象并将其赋值给一个变量名(即函数名)。
return用于返回结果对象,其为可选;无return语句的函数自动返回None对象。返回多个值时,彼此间使用逗号分隔,且组合为元组形式返回一个对象。
def语句运行之后,可以在程序中通过函数后附加括号进行调用。如:
1 2 3 4 5 6 | In [ 1 ]: def printName(): #定义一个函数printName ...: print "hello" ...: In [ 2 ]: printName() #调用函数printName hello |
Python创建、改变或查找变量名,都是在名称空间中进行的。名称空间(作用域)说白了就是一个变量所能够生效的范围。
在Python中,变量名在程序代码中被赋值的位置,决定了其能够被访问到的范围。
函数定义了本地作用域,而模块定义了全局作用域
每个模块都是一个全局作用域,因此,全局作用域的范围仅限于单个文件;
每次对函数的调用都会创建一个新的本地作用域,赋值的变量除非声明为全局变量,否则均为本地变量;
所有的变量名都可以归纳为本地、全局或内置的(由__builtin__模块提供)
变量名引用分三个作用域进行:首先是本地、之后是函数内、接着是全局,最后是内置。
变量名解析遵循由内而外的法则,也即LEGB原则(如上图)。变量解析时有几点需要注意:
先从Local(function)中查找,找到则返回解析结果,若找不到则到Enclosing function locals中查找;
若在Enclosing function locals中找到了则返回解析结果,若找不到则到Global(module)中查找;
若在Global(module)中找到了则返回解析结果,若找不到则到Built-in中查找;
若在Built-in中找到了则返回解析结果,若找不到则返回“名称引用错误”异常
变量名解析时,作用域越小,其优先级越高。也就是说,若在Local(function)中找到了,则直接返回其解析结果,而不会再到Enclosing function locals上去查找了。
全局变量:
全局变量是模块文件内部的顶层变量名;
如果要在函数内部对全局变量进行赋值的话,必须事先使用global进行声明,global关键字后跟一个或多个由逗号隔开的变量名;
全局变量名在函数内部不经声明也可以被引用;
本地变量在函数返回时会消失,而全局变量不会,因此,使用全局变量是保存函数状态信息的最直接办法;
尽量避免在函数中使用global声明全局变量。
内嵌函数:
Python中函数可以嵌套,也即可以在一个函数内部定义一个函数;
在函数内部定义的函数仅能由外层函数进行调用;
而如果外层函数直接把内层函数使用return返回,则调用外层函数时赋值的变量名引用的是一个内层函数对象,且此内层函数能够记忆其内部引用的外层函数的变量,这种行为叫做工厂函数,也叫闭合函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | In [ 11 ]: def maker(N): ...: def action(X): ...: return X * * N ...: return action ...: In [ 12 ]: f = maker( 2 ) In [ 13 ]: f( 3 ) Out[ 13 ]: 9 In [ 14 ]: f( 4 ) Out[ 14 ]: 16 In [ 15 ]: type (f) Out[ 15 ]: function |
参数传递:
参数的传递是通过自动将对象赋值给本地变量实现的
不可变参数通过“值”进行传递,在函数内部改变形参的值,只是让其引用了另一个对象(如数字)
可变参数通过“指针”进行传递,在函数内部改变形参的值,将直接修改引用的对象(如列表)
有两种方式可避免可变参数被函数修改:
1、直接传递可变对象的副本:
1 2 3 4 5 6 7 8 9 10 11 12 | In [ 16 ]: L = [ 1 , 2 , 3 , 4 ] In [ 17 ]: def changer(X): ...: X[ 2 ] + = 10 ...: return X ...: In [ 18 ]: changer(L[:]) Out[ 18 ]: [ 1 , 2 , 13 , 4 ] In [ 19 ]: print L [ 1 , 2 , 3 , 4 ] |
2、在函数内部创建可变参数的副本
1 2 3 4 5 6 7 8 9 10 11 12 13 | In [ 27 ]: L = [ 1 , 2 , 3 , 4 ] In [ 28 ]: def changer(X): ...: aa = X[:] ...: aa[ 2 ] + = 10 ...: return aa ...: In [ 29 ]: changer(L) Out[ 29 ]: [ 1 , 2 , 13 , 4 ] In [ 30 ]: print L [ 1 , 2 , 3 , 4 ] |
参数匹配模型:
默认情况下,参数通过其位置进行传递,从左至右,这意味着,必须精确地传递和函数头部参数一样多的参数
但也可以通过关键字参数、默认参数或参数容器等改变这种机制
位置:从左至右
关键字参数:使用“name=value”的语法通过参数名进行匹配
默认参数:定义函数时使用“name=value”的语法直接给变量一个值,从而传入的值可以少于参数个数
可变参数:定义是为了整合,调用是为了分解
定义函数时使用*开头的参数,可用于收集任意多基于位置的参数:
1 2 3 4 5 6 7 8 9 | In [ 31 ]: def test( * x): ...: print x ...: In [ 32 ]: test( 1 ) ( 1 ,) In [ 33 ]: test( 1 , 2 , 3 ) ( 1 , 2 , 3 ) |
定义函数时使用**开头的参数,可用于收集任意多基于关键字参数,并将其转换成字典:
1 2 3 4 5 6 | In [ 34 ]: def dict ( * * x): ...: print x ...: In [ 35 ]: dict (x = 1 ,y = 2 ,z = 6 ) { 'y' : 2 , 'x' : 1 , 'z' : 6 } |
混用位置参数与可变参数:
1 2 3 4 5 6 | In [ 36 ]: def a(x, * y): ...: print x,y ...: In [ 37 ]: a( 1 , 2 , 3 , 4 ) 1 ( 2 , 3 , 4 ) |
1 2 3 4 5 6 7 8 | In [ 38 ]: def b( * x, * * y): ...: print x ...: print y ...: In [ 39 ]: b( 1 , 2 , 3 , 4 , 5 ,i = 3 ,j = 9 ,c = 5 ) ( 1 , 2 , 3 , 4 , 5 ) { 'i' : 3 , 'c' : 5 , 'j' : 9 } |
可变参数解包:调用函数时,使用*开头的参数,可用于将参数集合打散,从而传递任意多基于位置或关键字的参数。可以理解为分解参数。
变量分解赋值:
1 2 3 4 5 6 7 8 9 10 11 12 | In [ 1 ]: l1 = [ 'Sun' , 'Mon' , 'Tue' ] In [ 2 ]: x,y,z = l1 In [ 3 ]: print x Sun In [ 4 ]: print y Mon In [ 5 ]: print z Tue |
1 2 3 4 5 6 7 8 | In [ 6 ]: l2 = [ 'Sun' , 'Mon' , 'Tue' ] In [ 7 ]: def a(x,y,z): ...: print x,y,z ...: In [ 8 ]: a( * l2) Sun Mon Tue |
可变参数与位置参数混合使用时,不论是定义还是调用,均使用以下顺序:
位置参数-->任意位置参数(*)-->关键字参数(**)
匿名函数lambda:
语法如下:
1 | lambda args: expression |
args:以逗号分隔的参数列表
expression:用到args中各参数的表达式
lambda语句定义的代码必须是合法的表达式,不能出现多条件语句(可使用if的三元表达式)和其它非表达式语句,如for和while等。
lambda的首要用途是指定短小的回调函数。lambda将返回一个函数而不是将函数赋值给某变量名。
注意:
lambda是一个表达式而非语句;
lambda是一个单个表达式,而不是一个代码块
1 2 3 4 5 6 7 8 9 | In [ 9 ]: lambda x,y: print x,y #不能是语句,只能是表达式 File "<ipython-input-9-323c5186b793>" , line 1 lambda x,y: print x,y ^ SyntaxError: invalid syntax In [ 10 ]: lambda x,y: x + y #返回的是一个函数 Out[ 10 ]: <function __main__.< lambda >> |
因为lambda返回的是一个函数,所以如果要调用它的话,则要将lambda表达式赋予一个变量名,通过变量名来调用:
1 2 3 4 | In [ 11 ]: a = lambda x,y: x + y In [ 12 ]: a( 5 , 6 ) Out[ 12 ]: 11 |
正因为lambda函数调用时必须赋予给某个变量名,通过变量名来调用,而这个变量名又不固定,故此称之为匿名函数。
def语句创建的函数将赋值给某变量名,而lambda表达式则直接返回函数。lambda也支持使用默认参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | In [ 13 ]: def testFunc(x,y,z): ...: return x + y + z ...: In [ 14 ]: testFunc( 4 , 5 , 6 ) Out[ 14 ]: 15 In [ 15 ]: f = lambda x,y,z: x + y + z In [ 16 ]: f( 4 , 5 , 6 ) Out[ 16 ]: 15 In [ 17 ]: f2 = ( lambda x,y,z = 10 : x + y + z) In [ 18 ]: f2( 4 , 5 ) Out[ 18 ]: 19 |
在对某个数据对象作出多种不同处理时,可以把每一种处理机制定义成一个lambda,而后对这个数据对象调用这多个lambda表达式。
1 2 3 4 5 6 7 | In [ 1 ]: l3 = [ ( lambda x: x * 2 ),( lambda y: y * 3 ) ] In [ 2 ]: for i in l3: #i取到的是lambda表达式,而不是x和y的值 ...: print i( 4 ) ...: 8 12 |
Python中的函数递归:
说到递归,什么是递归呢?
递归其实就是在运行中一层一层调用自己,直到不符合条件时退出。
递归对自身的调用有以下几点需要注意:
需要有一个退出条件(或者说边界条件)能够使自己退出递归状态,否则将会陷入无限式递归;
需要有一个递归前进段来定义递归怎么一层层往前走;
需要有一个递归返回段来定义必要时怎么一层层返回回来
递归的次数不能过多,在python中函数的递归最深层次不能超过1000层,否则将抛出异常
通过递归的方式实现阶乘:
10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1
==> 10 * (10-1) * ((10-1)-1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | In [ 4 ]: def factorial(x): ...: if x < = 1 : ...: return 1 ...: else : ...: return x * factorial(x - 1 ) ...: ...: In [ 5 ]: factorial( 3 ) #factorial(3) = 3 * factorial(2) = 3 * 2 * factorial(1) = 3 * 2 * 1 Out[ 5 ]: 6 In [ 6 ]: factorial( 10 ) Out[ 6 ]: 3628800 In [ 7 ]: 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 Out[ 7 ]: 3628800 |
通过递归的方式实现斐波那契数列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | In [ 11 ]: def fab(x): ...: if x = = 1 : ...: return 1 ...: elif x = = 0 : ...: return 0 ...: else : ...: result = int (fab(x - 1 ) + fab(x - 2 )) ...: return result ...: In [ 12 ]: for i in range ( 10 ): ...: print fab(i) ...: 0 1 1 2 3 5 8 13 21 34 |
Python中的函数设计规范:
耦合性:
通过参数接受输入,通过return产生输出以保证函数的独立性,除非必要,不要用print输出太多东西;
尽量减少使用全局变量进行函数间通信;
不要在函数中直接修改可变类型的参数;
避免直接改变定义在另一个模块中的变量
聚合性:
每个函数都应该有一个单一的、统一的目标;
每个函数的功能都应该相对简单,不要让一个函数的功能过多
Python中的函数执行环境:
函数可以通过多种方法获得输入及产生输出