19、闭包
嵌套定义函数
在函数内部定义函数
def outter():
def inner():
print('Inside inner')
print('Inside outter')
inner()
outter()
实现信息隐藏
def outter():
def inner():
print('inside inner')
print('inside outter')
inner()
# 这里将会报错。因为函数 inner 对外部是不可见的
inner()
实现信息隐藏的例子
函数 f1、f2、f3 是用于实现 complex 的辅助函数,我们希望它们仅仅能够被 complex 调用,而不会被其它函数调用。如果可以将函数 f1、f2、f3 定义在函数 complex 的内部,如下所示:
def complex():
def f1():
print('Inside f1')
def f2():
print('Inside f2')
def f3():
print('Inside f3')
print('Inside complex')
f1()
f2()
f3()
内部函数访问外部函数的局部变量
def outter():
local = 123
def inner():
print('Inside inner, local = {}'.format(local))
inner()
outter()
局部变量的生命周期
通常情况下,函数执行完后,函数内部的局部变量就不存在了。在嵌套定义函数的情况下,如果内部函数访问了外部函数的局部变量,外部函数执行完毕后,内部函数仍然可以访问外部函数的局部变量。示例代码如下:
def outter():
local = 123
def inner():
print('Inside inner, local = %d ' % local)
return inner
closure = outter()
closure()
调用函数 closure() 时,外部函数 outter 已经执行完,外部函数 outter 将内部函数 inner 返回并保存到变量 closure。调用函数 closure() 相当于调用内部函数 inner(),因此,在外部函数 outter 已经执行完的情况下,内部函数 inner 仍然可以访问外部函数的局部变量 local。
闭包的概念
对照闭包的定义,外部函数定义了局部变量 local,引用了局部变量 local 的内部函数 inner 就是闭包。闭包的独特之处在于:外部函数 outter 创造了局部变量 local, 即使外部函数 outter 已经执行完,内部函数 inner 仍然可以继续访问它引用的局部变量 local。
闭包的应用
概述
闭包经常用于 GUI 编程的事件响应处理函数。编程语言 Javascript 被用于浏览器的用户界面交互,使用 Javascript 编写事件响应处理函数时,闭包也是经常提及的知识点。
Tk 简介
Tkinter 是 Python 的标准 GUI 库,Python 使用 Tkinter 可以快速的创建 GUI 应用程序。
例子 1:显示一个窗口
import tkinter
root = tkinter.Tk()
root.mainloop()
若提示模块错误,可以在终端中输入如下命令:
sudo apt-get install python3-tk
python3 test.py
之后即可开启窗口
例子 2:显示一个 button
import tkinter
root = tkinter.Tk()
button = tkinter.Button(root, text = 'Button')
button.pack()
root.mainloop()
例子 3:为 button 增加一个事件处理函数
import tkinter
def on_button_click():
print('Button is clicked')
root = tkinter.Tk()
button = tkinter.Button(root, text = 'Button', command = on_button_click)
button.pack()
root.mainloop()
如何实现计算器
为了区分是哪一个按键被点击了,可以为不同的按键设定不同的按键处理函数,如下所示:
import tkinter
def on_button0_click():
print('Button 0 is clicked')
def on_button1_click():
print('Button 1 is clicked')
def on_button2_click():
print('Button 2 is clicked')
root = tkinter.Tk()
button0 = tkinter.Button(root, text = 'Button 0', command = on_button0_click)
button0.pack()
button1 = tkinter.Button(root, text = 'Button 1', command = on_button1_click)
button1.pack()
button2 = tkinter.Button(root, text = 'Button 2', command = on_button2_click)
button2.pack()
root.mainloop()
显然,这样的方式是很不合理的,在一个完整的计算器程序中,存在 20 多个按键,如果对每个按键都编写一个事件处理函数,就需要编写 20 多个事件处理函数。在下面的小节中,通过使用闭包解决这个问题。
例子 4:使用闭包为多个 button 增加事件处理函数
在上面的小节中,面临的问题是:需要为每个 button 编写一个事件处理函数。本小节编写一个事件处理函数响应所有的按键点击事件,代码如下:
import tkinter
def build_button(root, i):
def on_button_click():
print('Button %d is clicked' % i)
title = 'Button ' + str(i)
button = tkinter.Button(root, text = title, command = on_button_click)
button.pack()
root = tkinter.Tk()
# 使用 for 循环调用 build_button 创建 3 个 button
for i in range(3):
build_button(root, i)
root.mainloop()