python-内嵌函数和闭包

一、概述

Python中,是一切皆对象的,函数当然也是,因此函数内部是可以再嵌套函数的,这一点下面可以举例子说明。
再延伸一点,就能引申出一个高级概念了:[闭包](http://baike.baidu.com/link?url=rrHziqwEvt5X-2w76dqfp47N1pG7H8wKEKdXLmiLqbN0WXgFeXKfM8pPJNzuIBEsju-aNris6NK0vI6IH_hx2ci1OaqZ8hYYPhrySwmBof_)。嵌套函数在其外部函数(但不是全局区域)内使用,那么这个嵌套函数就会被认为是闭包。
如前所述,闭包是一个高级概念,一般在函数式编程(一种编程范式,与面向对象编程和面向过程编程是同级别的)中提到的比较多。Python也可以实现函数式编程。本文采用的是python3.5。

二、内嵌函数

先举个例子吧:
def func1(x):
    def func2(y):
        return x*y
    return func2
print(func1(3)(4))

如上例子所示,func2就是定义在func1中的函数,所以他是一个内嵌函数,在func1返回的时候是直接返回的函数名的,为什么可以这样呢?因为在函数式编程中,认为函数本身就是一个对象,我们现在不过是将对象返回了而已,当然可以做到。

为方便起见,如果定义两个入参为空的函数,如下所示:

def func1():
    x=6
    def func2():
        x *= x
        return x
    return func2
print(func1()())

系统就会报错:

Traceback (most recent call last):
  File "", line 8, in 
    print(func1()())
  File "", line 4, in func2
    x *= x
UnboundLocalError: local variable 'x' referenced before assignment

其实,在python的函数中和全局同名的变量,如果你有修改变量的值就会变成局部变量,在修改之前对该变量的引用自然就会出现没定义这样的错误了,如果确定要引用全局变量,并且要对它修改,就有两种方式了:
(1).加上global关键字;
(2).使用nonlocal关键字。
在此,试下nonlocal的方法:

def func1():
    x=6
    def func2():
        nonlocal x #global x
        x *= x
        return x
    return func2
print(func1())

输出为36

同样有一个疑问,如果在python2中,怎么办?Python2可是没有这些关键字的。怎么办?来分析一下:
之所以会报上述错误,还是因为程序都是默认存储在stack中的,函数结束后,内存就会释放,因此无法访问到。所以要想能访问到,必须换一个存储的地方,比如list。,那就有了修改的方法了,如下所示:

def func1():
    x=[6]
    def func2():
        x[0] *= x[0]
        return x[0]
    return func2
print(func1()())

或许Python热爱者觉得这种方法有些不太”正大光明”,因此才在python3中引入nonlocal这个了不起的关键字吧。

三、闭包

如二可知,一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。在Python中,所谓的闭包是一个包含有环境变量取值的函数对象。环境变量取值被保存在函数对象的closure属性中。比如下面的代码:

print(func1().__closure__)

输出:None

下面再看一个闭包提高代码可复用性的例子:

def line_conf(a, b):
    def line(x):
        return a*x + b
    return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))

这个例子中,函数line与环境变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。

你可能感兴趣的:(python学习,大数据学习)