闭包简述
闭包概念:在一个内部函数中,对外部作用域的变量进行引用, (并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包
(colsure)
。
<br/>
函数引用
在Python中,万物皆对象,因此函数也不例外。函数的名称可以当做变量使用。来看看下面几个例子:
In [2]: def func1():
...: print("func1() called")
...:
In [3]: type(func1)
Out[3]: function
In [4]: func1
Out[4]: <function __main__.func1()>
# 函数引用赋值
In [5]: func = func1
In [6]: id(func)
Out[6]: 1875091721816
In [7]: id(func1)
Out[7]: 1875091721816
# 函数调用
In [8]: func()
func1() called
In [9]: func1()
func1() called
<br/>
模拟事件处理范例
方案一、if判断
# -------------------- 函数引用的范例 -------------------- #
import random
def onclick():
"""点击事件处理"""
print("onclick called")
def onhover():
"""悬浮事件处理"""
print("onhover called")
def onmove():
"""移动事件处理"""
print("onmove called")
def quit():
"""关闭事件处理"""
print("quit called")
def handle_event1():
"""处理事件方案1"""
# 事件列表
events = ["click", "dbclick", "hover", "move", "quit"]
while True:
# 随机事件
event = random.choice(events)
print("event:", event)
if event == "click":
onclick()
elif event == "dbclick":
# ondbclick()
pass
elif event == "hover":
onhover()
elif event == "move":
onmove()
elif event == "quit":
quit()
break
# elif event == "..."
print()
<br/>
运行结果
event: move
onmove called
event: hover
onhover called
event: click
onclick called
event: quit
quit called
<br/>
方案二、函数引用
# 事件处理函数的引用对照字典
# 事件event为 key, 对应的事件处理函数引用为 value
event_dict = {
"click": onclick,
"hover": onhover,
"move": onmove,
"quit": quit
}
def handle_event2():
"""处理事件方案2"""
# 事件列表
events = ["click", "dbclick", "hover", "move", "quit"]
while True:
# 随机事件
event = random.choice(events)
# 获取事件处理函数
event_func = event_dict.get(event, None)
print("event:", event)
# 判断是否从字典中获取到事件函数
if event_func:
event_func()
print()
if event == "quit":
break
运行结果
event: hover
onhover called
event: hover
onhover called
event: hover
onhover called
event: click
onclick called
event: quit
quit called
使用函数引用,发现程序结构更简单明了,实现也非常巧妙。
<br/>
Python 中闭包的使用
def func(number):
# 在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,
# 那么将这个函数以及用到的一些变量称之为闭包
def func_in(number_in):
print("in func_in 函数, number_in is %d" % number_in)
return number + number_in
# 这里返回的就是闭包
return func_in
# 给func函数赋值,这个20就是给参数number
ret = func(20)
# 注意这里的100其实给参数number_in
print(ret(100))
#注 意这里的200其实给参数number_in
print(ret(200))
<br/>
运行结果
in func_in 函数, number_in is 100
120
in func_in 函数, number_in is 200
220
<br/>
闭包案例 - 直线方程配置
# -------------------- 直线方程配置 -------------------- #
def line_conf(a, b):
def line(x):
print("x =", x)
return a*x + b
print("y = {}*x + {}".format(a, b))
return line
# y = 2*x + 1
line1 = line_conf(2, 1)
print("y =", line1(5), "\n")
# y = 4*x + 5
line2 = line_conf(4, 5)
print("y =", line2(5), "\n")
<br/>
运行结果
y = 2*x + 1
x = 5
y = 11
y = 4*x + 5
x = 5
y = 25
这个例子中,函数 line
与变量 a,b
构成闭包。在创建闭包的时候,我们通过 line_conf
的参数 a,b
说明了这两个变量的取值,这样,我们就确定了函数的最终形式 ( y = 2x + 1和 y = 4x + 5)。我们只需要变换参数 a,b
,就可以获得不同的直线表达式函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明 a, b, x
。这样,我们就需要更多的参数传递,也减少了代码的可移植性。
注意: 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
<br/>
修改外部函数中的变量
In [23]: def counter(start=0):
...: def incr():
...: # nonlocal start
...: start += 1
...: return start
...: return incr
...:
In [24]: c = counter(5)
In [25]: c()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-25-cd4a6ef0eda0> in <module>
----> 1 c()
<ipython-input-23-7cfd2d33ae43> in incr()
2 def incr():
3 # nonlocal start
----> 4 start += 1
5 return start
6 return incr
UnboundLocalError: local variable 'start' referenced before assignment
local variable 'start' referenced before assignment
变量没有被赋值直接引用了,可以发现在内部函数不可以直接使用外部函数变量。
<br/>
Python3的方法 - nonlocal关键字
# -------------------- Python3修改外部函数中的变量 -------------------- #
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
# ipython 测验
In [12]: c1 = counter(5)
In [13]: c1()
Out[13]: 6
In [14]: c1()
Out[14]: 7
In [15]: c2 = counter(50)
In [16]: c2()
Out[16]: 51
In [17]: c2()
Out[17]: 52
In [18]: c1()
Out[18]: 8
In [19]: c1()
Out[19]: 9
In [20]: c2()
Out[20]: 53
In [21]: c2()
Out[21]: 54
<br/>
Python2的方法 - 列表
# -------------------- Python2修改外部函数中的变量 -------------------- #
def counter(start=0):
count = [start]
def incr():
count[0] += 1
return count[0]
return incr
# ipython 测验
In [31]: c1 = counter(5)
In [32]: c1()
Out[32]: 6
In [33]: c1()
Out[33]: 7
In [34]: c2 = counter(100)
In [35]: c2()
Out[35]: 101
In [36]: c2()
Out[36]: 102
<br/>
源代码
源代码已上传到 Gitee
PythonKnowledge: Python知识宝库,欢迎大家来访。
✍ 码字不易,万水千山总是情,点赞再走行不行,还望各位大侠多多支持❤️
<br/>
公众号
新建文件夹X
大自然用数百亿年创造出我们现实世界,而程序员用几百年创造出一个完全不同的虚拟世界。我们用键盘敲出一砖一瓦,用大脑构建一切。人们把1000视为权威,我们反其道行之,捍卫1024的地位。我们不是键盘侠,我们只是平凡世界中不凡的缔造者 。