免费发布信息
微信公众号

【python教程】Python 闭包简介

   来源:黔优网责任编辑:优优  时间:2023-08-26 10:03:15 浏览量:53
导读:Python 闭包简介闭包是较难理解的概念,Python 初学者可以暂时跳过此节。学习此节时需要理解 “函数是第一类对象” 的概念,在词条 “Python 的 lambda 表达式” 中详细介绍了这一概念。本节首先讲解理解闭包所需要

Python 闭包简介

闭包是较难理解的概念,Python 初学者可以暂时跳过此节。学习此节时需要理解 “函数是第一类对象” 的概念,在词条 “Python 的 lambda 表达式” 中详细介绍了这一概念。

本节首先讲解理解闭包所需要的铺垫知识,最后再引入闭包的定义。

1. 嵌套定义函数

1.1 在函数内部定义函数

Python 允许嵌套定义函数,可以在函数中定义函数,例如:

def outter():def inner():print('Inside inner')print('Inside outter')inner()outter()
  • 在第 1 行,定义函数 outter

  • 在第 2 行,在函数 outter 内部,定义函数 inner

  • 在第 6 行,在函数 outter 内部,调用函数 inner

函数 inner 定义在函数 outter 中,被称为函数嵌套定义。运行程序,输出结果如下:

Inside outterInside inner

1.2 实现信息隐藏

定义在函数内部的函数,对外是不可见的,例如:

def outter():def inner():print('inside inner')print('inside outter')            inner()inner()
  • 在第 1 行,定义了外部函数 outter

  • 在第 2 行,定义了内部函数 inner

  • 在第 6 行,在函数 outter 中,调用函数 inner

  • 在第 8 行,调用函数 inner

程序运行,输出如下:

Traceback (most recent call last):  File visible.py, line 8, in inner()NameError: name 'inner' is not defined

在第 4 行,试图调用定义在函数 outter 内部定义的函数 inner,程序运行时报错:name ‘inner’ is not defined,即找不到函数 inner。

因为函数 inner 是定义在函数 outter 内部的,函数 inner 对外部是不可见的,因此函数 outter 向外界隐藏了实现细节 inner,被称为信息隐藏。

1.3 实现信息隐藏的例子

实现一个复杂功能的函数时,在函数内部定义大量的辅助函数,这些辅助函数对外不可见。例如,假设要实现一个函数 complex,函数的功能非常复杂,将函数 complex 的功能分解为 3 个子功能,使用三个辅助函数 f1、f2、f3 完成对应的子功能,代码如下:

def f1():print('Inside f1')def f2():print('Inside f2')def f3():print('Inside f3')def complex():print('Inside complex')f1()f2()f3()
  • 在第 1 行,定义了辅助函数 f1

  • 在第 4 行,定义了辅助函数 f2

  • 在第 7 行,定义了辅助函数 f3

  • 在第 10 行,定义了主函数 complex,它通过调用 f1、f2、f3 实现自己的功能

在以上的实现中,函数 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()
  • 在第 2 行,在函数 complex 内部定义函数 f1

  • 在第 4 行,在函数 complex 内部定义函数 f2

  • 在第 6 行,在函数 complex 内部定义函数 f3

  • 在第 10 行到第 12 行,调用 f1、f2、f3 实现函数 complex 的功能

2. 内部函数访问外部函数的局部变量

嵌套定义函数时,内部函数可能需要访问外部函数的变量,例子代码如下:

def outter():local = def inner(local):print('Inside inner, local = %d', local)inner(local)outter()
  • 在第 1 行,定义了外部函数 outter

  • 在第 2 行,定义了函数 outter 的局部变量 local

  • 在第 4 行,定义了内部函数 inner

    • 函数 inner 需要访问函数 outter 的局部变量 local

  • 在第 7 行,将函数 outter 的局部变量 local 作为参数传递给函数 inner

    • 在第 5 行,函数 inner 就可以访问函数 outter 的局部变量 local

程序运行结果如下:

Inside inner, local = 123

在上面的例子中,将外部函数 outter 的局部变量 local 作为参数传递给内部函数 inner。Python 允许内部函数 inner 不通过参数传递直接访问外部函数 outter 的局部变量,简化了参数传递,代码如下:

def outter():local = def inner():print('Inside inner, local = %d', local)inner()
  • 在第 1 行,定义了外部函数 outter

  • 在第 2 行,定义了函数 outter 的局部变量 local

  • 在第 4 行,定义了内部函数 inner

    • 函数 inner 需要访问函数 outter 的局部变量 local

  • 在第 5 行,函数 inner 可以直接访问函数 outter 的局部变量 local

  • 在第 7 行,不用传递参数,直接调用函数 inner()

3. 局部变量的生命周期

通常情况下,函数执行完后,函数内部的局部变量就不存在了。在嵌套定义函数的情况下,如果内部函数访问了外部函数的局部变量,外部函数执行完毕后,内部函数仍然可以访问外部函数的局部变量。示例代码如下:

def outter():local = def inner():print('Inside inner, local = ' % local)return inner closure = outter()closure()
  • 在第 1 行,定义了外部函数 outter

  • 在第 2 行,定义了函数 outter 的局部变量 local

  • 在第 4 行,定义了内部函数 inner

    • 函数 inner 需要访问函数 outter 的局部变量 local

  • 在第 7 行,将函数 inner 作为值返回

  • 在第 9 行,调用函数 outter(),将返回值保存到变量 closure 中

  • 在第 10 行,调用函数 closure()

运行程序,输出结果如下:

Inside inner, local = 123

注意:在第 10 行,调用函数 closure() 时,外部函数 outter 已经执行完,外部函数 outter 将内部函数 inner 返回并保存到变量 closure。调用函数 closure() 相当于调用内部函数 inner(),因此,在外部函数 outter 已经执行完的情况下,内部函数 inner 仍然可以访问外部函数的局部变量 local。

4. 闭包的概念

闭包的英文是 closure,维基百科中闭包的严谨定义如下:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。—— 维基百科

在本节,以上一节具体的例子说明和理解闭包的概念,上一节的例子程序如下:

def outter():local = def inner():print('Inside inner, local = ' % local)return inner closure = outter()closure()
  • 在第 2 行,局部变量 local 就是自由变量

  • 在第 5 行,内部函数 inner 引用了局部变量 local (即自由变量)

因此,对照闭包的定义,外部函数定义了局部变量 local,引用了局部变量 local 的内部函数 inner 就是闭包。闭包的独特之处在于:外部函数 outter 创造了局部变量 local, 即使外部函数 outter 已经执行完,内部函数 inner 仍然可以继续访问它引用的局部变量 local。

5. 闭包的应用

5.1 概述

闭包经常用于 GUI 编程的事件响应处理函数。编程语言 Javascript 被用于浏览器的用户界面交互,使用 Javascript 编写事件响应处理函数时,闭包也是经常提及的知识点。

本小节通过编写一个简单的 Python GUI 程序,了解为什么需要使用闭包的语法特性,才方便实现功能需求。

5.2 Tk 简介

Tkinter 是 Python 的标准 GUI 库,Python 使用 Tkinter 可以快速的创建 GUI 应用程序。由于 Tkinter 是内置到 python 的安装包中,只要安装好 Python 之后就能使用 Tkinter 库。

由于 Tkinter 简单易学并且不需要安装,因此选择使用 Tk 编写应用闭包的例子程序。

5.3 例子 1:显示一个窗口

下面使用 Tk 编写一个显示窗口的程序,代码如下:

import tkinter root = tkinter.Tk()root.mainloop()
  • 在第 1 行,引入 Tk 库,Tk 库的名称是 tkinter

  • 在第 3 行,tkinter.Tk 方法会创建一个窗口 root

  • 在第 4 行,root.mainloop 方法等待用户的操作

运行程序,显示输出如下:

图片描述

5.4 例子 2:显示一个 button

下面使用 Tk 编写一个显示 button 的程序,代码如下:

import tkinter root = tkinter.Tk()button = tkinter.Button(root, text = 'Button')button.pack()root.mainloop()
  • 在第 4 行,tkinter.Button 方法创建一个新的 Button,它有两个参数:第一个参数 root,指定在 root 窗口中创建 Button;第二个参数 text,指定新创建 Button 的标签

  • 在第 5 行,button.pack 方法将 button 放置在 root 窗口中

运行程序,显示输出如下:

图片描述

5.5 例子 3:为 button 增加一个事件处理函数

当 button 被点击时,希望程序得到通知,需要为 button 增加一个事件处理函数,代码如下:

import tkinterdef on_button_click():print('Button is clicked')root = tkinter.Tk()button = tkinter.Button(root, text = 'Button', command = on_button_click)button.pack()root.mainloop()
  • 在第 3 行,定义了函数 on_button_click,当用户点击 button 时,程序得到通知,执行 on_btton_click

  • 在第 4 行,函数 on_button_click 在控制台打印输出 ‘Button is clicked’

  • 在第 7 行,tkinter.Button 创建一个 Button,设置 3 个参数

    • 参数 root,表示在 root 窗口中创建 button

    • 参数 text,表示 button 的标签

    • 参数 command,表示当 button 被点击时,对应的事件处理函数

  • 在第 8 行,root.mainloop 等待用户的操作,当用户点击 button 时,程序会执行 button 对应的事件处理函数,即执行 on_button_click

运行程序,显示输出如下:

图片描述


当用户点击 button 时,执行 on_button_click,在控制台中打印 ‘Button is clicked’,显示输出如下:

图片描述

5.6 如何实现计算器

由于篇幅,本节没有实现一个完整的计算器,在这里仅仅讨论实现计算器程序的关键要点。windows 自带的计算器的界面如下所示:

计算器向用户展示各种按钮,包括:

  • 数字按键,0、1、2、3、4、5、6、7、9

  • 运算符按键,+、-、*、\、=

用户在点击某个按键时,程序得到通知:按键被点击了,但是这样的信息还不够,为了实现运算逻辑,还需要知道具体是哪一个按键被点击了。

为了区分是哪一个按键被点击了,可以为不同的按键设定不同的按键处理函数,如下所示:

import tkinterdef 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_button0_click)button1.pack()button2 = tkinter.Button(root, text = 'Button 2', command = on_button0_click)button2.pack()root.mainloop()

为了节省篇幅,这里仅仅处理了 3 个按键。显然,这样的方式是很不合理的,在一个完整的计算器程序中,存在 20 多个按键,如果对每个按键都编写一个事件处理函数,就需要编写 20 多个事件处理函数。在下面的小节中,通过使用闭包解决这个问题。

5.7 例子 4:使用闭包为多个 button 增加事件处理函数

在上面的小节中,面临的问题是:需要为每个 button 编写一个事件处理函数。本小节编写一个事件处理函数响应所有的按键点击事件,代码如下:

import tkinterdef 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 i in range():build_button(root, i)root.mainloop()
  • 在第 11 行,tkinter.Tk 创建窗口 root

  • 在第 12 行,使用 for 循环调用 build_button 创建 3 个 button

  • 在第 14 行,root.mainloop 等待用户操作

  • 在第 3 行,定义函数 build_button 创建 1 个 button

    • 参数 root,表示在 root 窗口中创建 button

    • 参数 i,表示 button 的序号

  • 在第 4 行,定义事件处理函数 on_button_click

    • build_button 是外部函数

    • on_button_click 是内部函数

    • 在第 5 行,打印外部函数 build_button 的参数 i,因此 on_button_click 是一个闭包函数

  • 在第 7 行,根据 button 的序号 i 设置 button 的标签

  • 在第 7 行,创建一个 button,设置标签和事件处理函数

运行程序,显示输出如下:

图片描述


当用户点击不同的 button 时,都是执行 on_button_click,但在控制台中打印的字符串是不一样的,显示输出如下:

图片描述


在这个例子中,外部函数 build_button 提供了参数 i 用于区分 button,内部函数 on_button_click 可以访问外部函数的参数。因此,当 button 被点击时,通过参数 i 知道是哪一个 button 被点击了,编写 1 个事件处理函数就可以处理多个 button 的点击事件,即使用闭包就很自然的解决了实现计算器程序需要面临的问题。

6. 小结

从概念上来看这一个小节还是比较晦涩的,我在文章的开头也说过了初学者可以先跳过这一小节,等后面在转过头来学习。闭包这个概念非常的重要,面试中有很多面试官喜欢问闭包相关的问题,大家一定要多看几遍,彻底掌握闭包。

 
 
 
没用 0举报 收藏 0评论 0
免责声明:
黔优网以上展示内容来源于用户自主上传、合作媒体、企业机构或网络收集整理,版权争议与本站无关,文章涉及见解与观点不代表黔优网官方立场,请读者仅做参考。本文标题:【python教程】Python 闭包简介,本文链接:https://qianu.com/news/250744.html,欢迎转载,转载时请说明出处。若您认为本文侵犯了您的版权信息,或您发现该内容有任何违法信息,请您立即点此【投诉举报】并提供有效线索,也可以通过邮件(邮箱号:kefu@qianu.com)联系我们及时修正或删除。
 
 

 

 
推荐图文
推荐商业资讯