肉渣教程

函数

上一节 下一节

什么是函数?

先说一个数学公式吧,求半径为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就是自定义的一个函数,需要关注如下要点:

  • def是声明函数的关键词,有点像土匪下山打劫前总要先扯一面大旗子来告诉路人他们是土匪,def在这里也是同样的作用,就是在告诉程序,这里是在定义一个函数在;
  • 紧接着def后面的是自定义函数名,为了代码的逻辑性与可读性,函数名请起得直观与好理解一些;(另外函数名请不要python保留词、变量名称相同)
  • r是需要传入函数的形式参数,简称形参;(可以不设置形式参数,也可以设置多个形式参数)
  • 定义函数的时候,别忘了那个英文输入法下输出的冒号
  • 函数内可以使用函数外的全局变量,也可以在函数内声明局部变量,但局部变量无法在函数外生效;(上例中的s就是局部变量)
  • 形式参数也属于局部变量,其作用域仅仅是在函数内;
  • 函数内的代码会向右进行缩进,请统一以4个空格为标准;(别的编程语言有begin和end,或通过{}代表开始和结束,而python则是使用缩进来体现哪些语句是属于函数内的语句)
  • return语句是用来返回函数结果的,与此同时,函数终止,就算return后面还有属于函数内的代码,也同样终止运行。

构建函数

构建函数的格式:

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]

运行一下

使用lambda构建匿名函数

先举一个例子,以上述的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;解决这类异常有至少两种方式,一是手动提高系统上限,二是尾递归的方式。更多详情请查看这篇回答。(通常来说,尾递归是一种被推荐的方式;但是世界是辩证的,在此极其不推荐尾递归的方式。)


递归是一种非常重要与常用的算法,但这里说的还是比较简单的;如果想在算法上有一定造诣,一定要对递归再进行深入学习与实践。


函数

上一节 下一节