先说一个数学公式吧,求半径为r的球形的表面积,从数学领域的函数角度来说,会使用公式f(r) = 4 * 3.1415926 * r * r
;而在Python中的函数也有点类似,实现同样的功能如下所示:
# coding=utf-8 def ball_surface( r ): s = 4 * 3.1415926 * r * r return s # 测试一下上面构建的函数 print ball_surface( 6.66 ) print ball_surface( 100 )
上面的ball_surface就是自定义的一个函数,需要关注如下要点:
{
和}
代表开始和结束,而python则是使用缩进来体现哪些语句是属于函数内的语句)构建函数的格式:
def 函数名( 形式参数 ): 函数内代码块
示例1:
n = 3 def draw_line(): print "-" * 10 for i in xrange(10): print i if i % 2 == 1: draw_line()
示例2:
# coding=utf-8 # 数据初始化设定 num = 7 # 返回正整数n的阶乘结果 def factorial( n ): res = 1 for i in xrange( n ): res *= (i+1) return res # 依次输出1、2、3 ... num的阶乘结果 for i in xrange( num ): print i+1, factorial( i+1 )
示例3:
# coding=utf-8 # 数据初始化设定 point1_x, point1_y = 90, 100 point2_x, point2_y = 108, 7 # 根据两个点的横纵坐标,来求两点之间的距离 def get_distance( x1, y1, x2, y2): d = ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 return d print get_distance( point1_x, point1_y, point2_x, point2_y )
定义函数不代表该函数会主动运行;当函数被定义以后,调用函数才会根据调用时传入函数的形式参数,而执行函数内的代码。
如下例所示,在函数内部定义的变量s是局部变量;函数的形式参数x、y、z也属于局部变量。用次元的概念来说,每调用一次函数,就相当于建立一个虚拟的次元,当函数结束后,这个次元就会消失。这些局部变量只在当次函数运行中有效,也就是说局部变量与函数被执行的那个次元绑定,局部变量是突破不了那个次元的;那次函数执行结束,那个次元也就结束而消失了,与那个次元绑定的局部变量也才会被释放而消失;纵使是同一个函数被调用而生成的次元,次元与次元之间也是独立的。
a, b, c = 1, 2, 3 def get_sum( x, y, z ): s = x + y + z return s print get_sum( a, b, c )
函数中的局部变量尽量不要与函数外的全局变量名称重复,否则容易歧义;若真的重复了,函数内定义的那个变量依旧是局部变量,与函数外的同名称的全局变量没有关联,两者只是表皮一样;因此对于一些临时使用的变量,与全局变量重复也是无伤大雅的,不必苛求,例如循环变量。
a = "fou" def test(): a = "zhuan" print a test() print a
若非要在函数中使用全局变量,可在函数内使用global进行声明。(但是极其不建议在函数中使用全局变量)
a = "fou" def test(): global a a = "zhuan" print a test() print a
声明函数时,若提前设置了函数的默认值,则调用函数时,未必需要传入所有的形式参数,对于设置了默认取值的形参,可以缺省。
def test( a, b=1, c="zhuanfou" ): print a + b, c test( 9 ) test( 10 ) test( 1, 3 ) test( 1, 3, "-_-" ) test( 1, c="^_^" ) test( a=1, b=3, c="zhuanfou.com" )
九阴要诀第一式:化繁为简,分而击破
函数的思维是拆分,把复杂的问题拆分成独立的问题,用函数来解决每个独立的问题;而且函数是可以重复使用的,遇到相同的问题调用之即可。这不仅仅是函数的设计思维,更是编程的思维与艺术。
九阴要诀第二式:低耦合、高分离
江湖有传言“全局变量是魔鬼”,这其实说的也是“低耦合、高分离”的事情;若在大量在函数中使用全局变量,会导致各个函数的关联性太大,牵一发而动全身,此乃极其糟糕的一件事情。函数的设计,追求的是低耦合、高分离。(耦合的意思就是多者之间的关联性,低耦合就是高分离)
Python提供了很多已经定义好的函数,直接去使用即可。这样的函数,就被称作内置函数。(同样的功能,请尽量使用内置函数,而不要自己重新去写,一则没必要重新造轮子,二则python的内置函数中有很多都是用C进行重构过,效率是远远高于自己手动重写)
>>> len( "zhuanfou" ) 8 >>> sum( [1, 2, 3] ) 6 >>> max( [1, 2, 3] ) 3 >>> abs( -2 ) 2 >>> sorted( [3, 2, 1] ) [1, 2, 3]
先举一个例子,以上述的sorted函数为例,默认情况下,sorted函数是根据有序序列对象的元素大小进行排序的;不过sorted还有一个形参叫key,可以把一个函数传递给形参key,则排序的标准则根据形参key所存储的函数返回值进行排序,即将每个元素传入形参key代表的函数内,返回值即是排序的参照标准。举例如下:
def func(x): return abs(x - 7) my_list = [1, 9, 9, 0] sorted_list = sorted( my_list, key=func )
上述的sorted_list变量的值为[9, 9, 1, 0],因为排序是根据传入函数func所返回的结果为标准,9传入函数会返回2,1传入函数会返回6,0传入函数会返回7;所以参照此标准,排序的结果应为[9, 9, 1, 0]。
但是func函数其实是一个很简单的函数,单独为一个排序而创建函数,有点牛刀杀鸡的感觉;对于这类场景,就可以使用lambda来构建匿名函数,格式如下:
lambda 形参:返回的结果
将上述排序改成使用lambda匿名函数的方式,则如下所示:
my_list = [1, 9, 9, 0] sorted_list = sorted( my_list, key=lambda x:abs(x - 7) )
所谓的递归,简而言之就是函数调用函数自己;一来是将复杂的问题拆分得更加独立简单,二来执行每次函数就相当于生成一个独立的次元,即使是同一个函数生成的多个次元,次元与次元之间也是独立的。因此,处理很多比较复杂的问题,就可以在次元之间再请求函数而生成另一个次元,另一个次元里还可以再深一层去继续请求函数生成新的次元;就跟盗梦空间中的多层梦境很像。
使用递归算法时,一定要注意递归的结束,总不能一层一层梦境深入下去,这样就会陷入无穷梦境之中去。因此在开始递归前,就要制定好结束梦境的计划,当符合结束梦境的那个条件时,就立刻结束梦境,然后一层一层由深度梦境返回浅层梦境,最终返回现实,也就是递归彻底结束,函数返回结果。
以阶乘为例,n的阶乘是n! = n * (n-1) * ... * 2 * 1
,若用递归的思维则可把程式变成n! = n * (n-1)!
,而最终1的阶乘就等于1,结束递归,依次返回到上一层函数,最终获得n的阶乘结果。若使用python来实现上述递归函数,则定义函数factorial(n)
来返回正整数n的阶乘结果,则每次返回n * factorial(n-1)
即可。示例代码如下:
# coding=utf-8 # 阶乘函数,返回形参n的阶乘结果 def factorial(n): # 递归的终止条件 if n == 1: # 若n为1,则返回1 # 因为1的阶乘结果为1 return 1 # n大于1,返回n * factorial(n-1) return n * factorial(n-1) # 调用函数来试试 print factorial( 3 ) print factorial( 7 ) print factorial( 1 )
Python中递归的层数是有限制的;这是对系统资源的一种保护机制。若超出了限制,python则会抛出异常信息RuntimeError: maximum recursion depth exceeded
;解决这类异常有至少两种方式,一是手动提高系统上限,二是尾递归的方式。更多详情请查看这篇回答。(通常来说,尾递归是一种被推荐的方式;但是世界是辩证的,在此极其不推荐尾递归的方式。)
递归是一种非常重要与常用的算法,但这里说的还是比较简单的;如果想在算法上有一定造诣,一定要对递归再进行深入学习与实践。