本文目录
1、什么是闭包
1.1 一段简单的闭包函数代码
def outer1(outer_param):
def inner():
print("外部函数的参数是:%s" % outer_param) # outer_param是外部函数outer的参数,是一个外部的变量,并不在函数inner中
return inner # 此处返回的是函数对象!而不是函数调用(inner())的结果
1.2 闭包类似于内部函数,但有所不同
从形式上看,闭包类似于简单的内部函数。
# 一个简单的内部函数
def outer2(outer_param):
def inner(outer_param):
print("外部函数的参数是:%s" % outer_param)
return inner(outer_param)
从代码上看,闭包和普通内部函数的相似点有两个:
- 在函数(outer)内部定义了一个新函数(inner);
- 内部的新函数inner可以读取到外部函数的参数(outer_param)
然而细看代码,会发现他们也有几点不同,正是这几点不同让闭包成为了闭包:
1、虽然普通内部函数和闭包都可以读取到外部函数的参数(outer_param),但是其实二者读取到的方式是不同的!闭包是直接读取,
而普通内部函数中,inner是通过自己的参数读取到的。
也就是说:普通内部函数是 生成一个自己的内部变量,通过参数将外部变量的值赋值给这个内部变量,借以达到记录外部变量值的目的,而闭包则是直接访问了外部变量。
这样子改写一下代码,就能看出差异了:
# 一个简单的内部函数
def outer(outer_param):
def inner(inner_param):
print("外部函数的参数是:%s" % inner_param)
return inner(outer_param)
普通内部函数访问到外部函数的变量,是靠外部函数主动将变量以参数(inner_param)的形式传递给内部函数,而闭包则没有这个过程
2、普通内部函数返回的是调用内部函数后的结果,而闭包则是返回inner函数对象,尚未被调用执行。
# 普通的内部函数返回
a = outer2("hello")
>> 外部函数的参数是:hello
a
>> None # 内部函数只有print,没有返回值,所以为None
## 闭包函数
b = outer1("hello")
b
>> <function __main__.outer1.<locals>.inner()> # 函数对象
b()
>> 外部函数的参数是:hello # 此时才调用它
1.3 闭包的本质是:被动态创建的,可以记录外部变量的函数
闭包的定义:闭包是指一个可以由另一个函数动态生成的函数,并且可以改变和存储函数外创建的变量的值。
在前面的普通内部函数中,inner也可以被看作是一个闭包,但是外部函数并没有返回这个函数,而是直接返回了这个函数执行的结果,所以我们并不能感受到inner作为一个动态生成的函数的意义。
1.3.1 被动态生成
回顾下这段闭包函数的代码:
def outer1(outer_param):
def inner():
print("外部函数的参数是:%s" % outer_param)
return inner
每传入一个参数给外部函数并执行,这个函数都将返回一个新的inner,不同的inner函数对象,这就是“动态”的“生成”函数的过程。
a = outer1("hello")
b = outer1("world")
a
>> <function __main__.outer1.<locals>.inner()>
b
>> <function __main__.outer1.<locals>.inner()>
而普通的内部函数是不具有这个过程中的。首先,它是通过参数传递的形式记录了外部变量的值,函数还是那个函数本身,只是每次调用执行的实际参数的值不一样了,并没有“生成”的概念;其次,普通内部函数没有返回inner函数的对象,只是把这个函数执行了一边。
1.3.2 记录外部变量
这个特性就比较明显了。从执行流上看,返回inner函数对象时,外部变量outer_param已经存进了这个函数对象里面。
在调用的时候会显式的感受到:
a = outer1("hello")
b = outer1("world")
a
>> <function __main__.outer1.<locals>.inner()> # 可以抽象的理解为,这个函数对象里面有一个out_param的变量 = hello
b
>> <function __main__.outer1.<locals>.inner()> # 而这个函数对象里面也有一个out_param的变量 = world
# 当我们去调用这两个函数对象时,对应的值都可以显式的访问到
a()
>> 外部函数的参数是:hello
b()
>> 外部函数的参数是:world
2、闭包的应用示例:装饰器函数
装饰器(decorator)本质上也是一种函数,它把一个函数作为输入,同时返回一个新函数。关于装饰器的详细内容,请参见另外一篇博客:
这里仅仅强调一下装饰器中和闭包有关的部分,先看一段装饰器的示例代码:
def my_decorator(func):
'''
这个装饰器只是简单地输出了输入的函数名字,同时执行了这个函数
'''
def new_func(*args,**kwargs):
print(func.__name__) # 这里直接访问了外部变量func
return func(*args,**kwargs)
return new_func # 这里返回了一个新的函数!对象!
闭包在其中体现在两个方面:
1、内部函数new_func直接访问了外部变量func,并且输出了它的__name__属性
2、装饰器函数decorator返回了一个在其内部定义的函数对象new_func