嵌套函数,也是函数作为对象的体现:可以作为函数返回值,即 return 语句中的对象。
>>> def out():
... def inner():
... print("this is inner.")
... print("this is out.")
... return inner
...
这里定义了两个函数:out()
和 inner()
。只不过这两个函数不是并列关系,而是函数 inner()
在函数 out()
里面,像这样的称为嵌套函数。
>>> inner()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'inner' is not defined
函数 inner()
没有在当前位置被定义(其原因参阅7.3.3节)。
>>> out()
this is out.
<function out.<locals>.inner at 0x7fe70f9430d0>
执行了 out()
函数之后,将其内部定义的函数对象作为返回值返回到当前位置。跟其他函数返回值一样,也可以用一个变量引用返回的函数对象 inner
。
>>> f = out()
this is out.
>>> f
<function out.<locals>.inner at 0x7fe70f9431f0>
>>> f()
this is inner.
变量 f
引用了函数对象 inner
,通过 f()
就可以调用(或执行)该对象,这样被定义在函数 out()
内部的 inner()
就能在当前位置执行了。
嵌套函数除了能说明函数是对象之外,还有别的什么用途吗?
有一类函数,能够产生其他函数,我们形象地称之为“工厂函数”(factory functions)。显然,嵌套函数就是工厂函数。
#coding:utf-8
'''
filename: nestfunc.py
'''
def power_generator(num):
def power_n(power):
return num ** power
return power_n
if __name__ == "__main__":
power_two = power_generator(2) # (4)
power_three = power_generator(3) # (5)
print(power_two(8)) # (6)
print(power_three(2))
此处定义了一个嵌套函数,函数 power_n()
实现了指数运算,但底数 num
是由外层函数 power_generator()
决定的,通过执行 power_generator()
能够得到计算指定底数的乘方运算函数,例如:
power_two()
计算 2^n ,其中 n 是 power_two()
的参数,也就是 power_n()
的参数 power
。如 power_two(8)
计算 2^8 ;power_two(10)
计算 2^{10} 等。当然,纯粹计算指数,别的方法也可行。
如果再深入分析这个工厂函数,还会发现“与众不同”之处。例如注释(4),形参 num
引用了整数 2
,当该函数执行完毕,num
依然存在,体现在注释(6)的 pwoer_two(8)
中。这是因为嵌套在 power_generator()
内部的函数 power_n()
引用了 num
。用一种形象的方法描述此过程,注释(4)得到了一个包裹——函数对象 power_n
,num
是一个封闭在此包裹中的变量,不论包裹被快递送到哪里——比如注释(6),num
都会形影不离(因为被封闭在里面了)。像 num
这样的变量(即参数),称之为自由变量——随着包裹可以自由移动。像 power_n()
这样的类似于包裹的函数,称之为闭包(Closure)。
闭包这个术语是由英国计算机科学家彼得·约翰·兰丁(Peter John Landin)于1964年提出,后来因为在 Scheme 语言中的应用而广为流传。在现代编程语言中,所有将函数作为第一类对象的都可以实现闭包,Python 语言就是典型代表。其他没有将函数作为第一类对象的语言,比如 Java ,若要使用闭包,则需要通过类或者接口。