返回函数和闭包

8.返回函数

Python的函数不但可以返回intstrlistdict等数据类型,还可以返回函数!

例如,定义一个函数 f(),我们让它返回一个函数 g,可以这样写:

def f():

    print 'call f()...'

    # 定义函数g:

    def g():

        print 'call g()...'

    # 返回函数g:

    return g

仔细观察上面的函数定义,我们在函数 f 内部又定义了一个函数 g。由于函数 g 也是一个对象,函数名 g 就是指向函数 g 的变量,所以,最外层函数 f 可以返回变量 g,也就是函数 g 本身。

调用函数 f,我们会得到 f 返回的一个函数:

>>> x= f()   # 调用f()

call f()...

>>>x   # 变量xf()返回的函数:

<function gat 0x1037bf320>

>>>x()   # x指向函数,因此可以调用

callg()...   # 调用x()就是执行g()函数定义的代码

请注意区分返回函数和返回值:

def myabs():

    return abs  # 返回函数

def myabs2(x):

    return abs(x)   # 返回函数调用的结果,返回值是一个数值

返回函数可以把一些计算延迟执行。例如,如果定义一个普通的求和函数:

defcalc_sum(lst):

    return sum(lst)

调用calc_sum()函数时,将立刻计算并得到结果:

>>>calc_sum([1, 2, 3, 4])

10

但是,如果返回一个函数,就可以延迟计算

defcalc_sum(lst):

    def lazy_sum():

        return sum(lst)

    return lazy_sum

# 调用calc_sum()并没有计算出结果,而是返回函数:

>>> f= calc_sum([1, 2, 3, 4])

>>> f

<functionlazy_sum at 0x1037bfaa0>

# 对返回的函数进行调用时,才计算出结果:

>>>f()

10

由于可以返回函数,我们在后续代码里就可以决定到底要不要调用该函数。

任务

请编写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。

9.闭包

在函数内部定义的函数和外部定义的函数是一样的,只是他们无法被外部访问:

def g():

    print 'g()...'

 

def f():

    print 'f()...'

    return g

 g 的定义移入函数 f 内部,防止其他代码调用g

def f():

    print 'f()...'

    def g():

        print 'g()...'

    return g

但是,考察上一小节定义的 calc_sum 函数:

defcalc_sum(lst):

    def lazy_sum():

        return sum(lst)

    return lazy_sum

注意发现没法把 lazy_sum 移到 calc_sum 的外部,因为它引用了 calc_sum 的参数 lst

像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure

闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:

# 希望一次返回3个函数,分别计算1x1,2x2,3x3:

def count():

    fs = []

    for i in range(1, 4):

        def f():

             return i*i

        fs.append(f)

    return fs

 

f1, f2, f3 =count()

你可能认为调用f1()f2()f3()结果应该是149,但实际结果全部都是 9(请自己动手验证)。

原因就是当count()函数返回了3个函数时,这3个函数所引用的变量的值已经变成了3。由于f1f2f3并没有被调用,所以,此时他们并未计算 i*i,当 f1 被调用时:

>>>f1()

9     # 因为f1现在才计算i*i,但现在i的值已经变为3

因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。

任务

返回闭包不能引用循环变量,请改写count()函数,让它正确返回能计算1x12x23x3的函数。

?不会了怎么办

考察下面的函数 f:

def f(j):

    def g():

        return j*j

    return g

它可以正确地返回一个闭包gg所引用的变量j不是循环变量,因此将正常执行。

co1unt函数的循环内部,如果借助f函数,就可以避免引用循环变量i


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章