Python闭包
闭包的概念:
闭包(Closure)是词法闭包(Lexical Closure)的简称,是函数式编程的重要语法结构。如果一个函数里定义了一个内部函数,这个函数引用了外部函数的相关参数或变量,外部函数最终把这个内部函数返回了,那么这个内部函数被称为闭包。举个例子:
def func(x):
def inner(y):
return x + y
return inner
上述例子中,在函数func中定义了一个inner函数,inner函数访问外部函数func的(参数x)变量,并且把inner函数作为返回值返回给func函数,inner函数就是闭包。运行下述代码:
f = func(2)
print(f)
print(f(8))
print(func(2)(8))
运行结果
<function func.<locals>.inner at 0x0000000006B4F158>
10
10
从结果中可以看到f是函数inner,而不是func,因为return返回的是inner函数,并且此时f函数包含了来自外部函数func的参数2,f函数实际表达式为 def inner(y):return 2 + y,当运行f(8)时,结果为 2 + 8 = 10。
闭包的作用:
闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例,这一点有点类似于面向对象中的类,其作用都是减少重复代码和提高功能的可扩展性。以下介绍两种闭包主要的用途:
(1)即使生成闭包的环境已经释放,闭包仍然存在,传给闭包的变量也不会被释放,根据这个特性可以改变外部作用域的局部变量来实现不同的功能,比如:现在有一组数据,既要计算每个数据的二次方,又要计算每个数据的三次方。示例如下:
from math import pow
data = [1,2,3,4,5,6]
def get_result(n):
def do_pow(x):
return pow(x,n)
return do_pow
pow_2 = get_result(2)
pow_3 = get_result(3)
for i in data:
pow_2(i)
pow_3(i)
(2)装饰函数。如需求:计算函数的运行时间。实现计算一个函数的运行时间只需在其内部添加代码即可,但当需要计算多个函数时,需要重复在其内部修改代码,重复性工作量大,且耦合度高。要解决此问题可以使用装饰器。示例如下:
import time
def run_time(func):
def wrapper(*args,**kwargs):
time_start = time.time()
func(*args,**kwargs)
time_end = time.time()
print("运行时间为{}秒".format(int(time_end-time_start)))
return wrapper
@run_time
def func(x):
time.sleep(x)
func(2)
运行结果:
运行时间为2秒
从示例中可以看出装饰器实际就是将一个函数传入另一个函数中,在另一个函数中增加其它功能,再传出来,实际已经变为另一个对象了,但是使用者觉察不出来。从中也可以看出装饰器的作用就是为已经存在的函数或对象添加额外的功能。
使用闭包注意点:
(1)闭包无法修改外部函数的局部变量。示例如下:
def func():
x = 0
def inner():
x = 1
print('inner x:',x)
print('func x before run inner:',x)
inner()
print('func x after run inner:', x)
func()
运行结果:
func x before run inner: 0
inner x: 1
func x after run inner: 0
从上述代码中可以看到外部函数局部变量在进入inner函数进行赋值 x = 1后依然为0,赋值前后并没有发生变化。
(2)闭包无法直接访问外部函数的局部变量。示例如下:
def func():
x = 10
def inner():
x = 2*x
print(x)
return inner
func()()
运行结果:
报错:UnboundLocalError: local variable 'x' referenced before assignment 在赋值之前引用局部变量'x'
解决方案:Python3中通过nonlocal关键字来解决。
def func():
x = 10
def inner():
nonlocal x #把x声明为非局部变量
x = 2*x
print(x)
return inner
func()()
运行结果:
20