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传递来的参数,通过闭包的形式,将最终函数确定下来。