如何理解及使用Python闭包


Posted in Python onJune 01, 2021

一、Python 中的作用域规则和嵌套函数

每当执行一个函数时,就会创建一个新的局部命名空间,它表示包含函数体内分配的函数参数和变量名的局部环境。我们可以将名称空间看作一个字典,其中键是对象名称,值是对象本身。

解析名称时,解释器首先搜索本地命名空间。如果不存在匹配,则搜索全局名称空间,该名称空间是定义函数的模块。如果仍然没有找到匹配项,则在引发 NameError 异常之前最终检查内置名称空间。下图说明了这一点:

如何理解及使用Python闭包

让我们考虑下面的例子:

age = 27
def birthday(): 
  age = 28
birthday()
print(age)  # age will still be 27
>>
27

当变量在函数内部赋值时,它们总是绑定到函数的本地名称空间; 因此,函数体中的变量 age 指的是一个包含值28的全新对象,而不是外部变量。可以使用全局语句更改此行为。下面的示例强调了这一点:

age = 27
name = "Sarah"
def birthday(): 
  global age       # 'age' is in global namespace 
  age = 28
  name = "Roark"
birthday()         # age is now 28. name will still be "Sarah"

Python 也支持嵌套函数定义(函数内部的函数):

def countdown(start):
  # This is the outer enclosing function
  def display():
    # This is the nested function
    n = start
    while n > 0:
      n-=1
      print('T-minus %d' % n)
 
  display()
# We execute the function
countdown(3)
>>>
T-minus 3
T-minus 2
T-minus 1

二、定义闭包函数

在上面的示例中,如果函数 countdown()的最后一行返回了 display 函数而不是调用它,会发生什么情况?这意味着该函数的定义如下:

def countdown(start):
  # This is the outer enclosing function
  def display():
    # This is the nested function
    n = start
    while n > 0:
      n-=1
      print('T-minus %d' % n)
  return display
# Now let's try calling this function.
counter1 = countdown(2)
counter1()
>>>
T-minus 2
T-minus 1

使用值2调用 countdown()函数,并将返回的函数绑定到名称 counter1。在执行 counter1()时,它使用最初提供给 countdown ()的 start 值。因此,在调用 counter1()时,尽管我们已经执行了 count1()函数,但仍然记住这个值。

这种将一些数据(本例中为2)附加到代码的技术在 Python 中称为闭包。

即使变量超出范围或函数本身从当前名称空间中移除,也会记住封闭范围中的这个值。我们可以尝试下面的代码来确认:

>>> del countdown
>>> counter1()
T-minus 2
T-minus 1
>>> countdown(2)
Traceback (most recent call last):
...
NameError: name 'countdown' is not defined

三、何时使用闭包?

当一个类中实现的方法很少(大多数情况下只有一个方法)时,闭包可以提供一个替代的、更优雅的解决方案。此外,如果我们希望根据延迟或延迟计算的概念编写代码,闭包和嵌套函数特别有用。下面是一个例子:

from urllib.request import urlopen
def page(url): 
  def get(): 
    return urlopen(url).read() 
  return get

在上面的示例中,page ()函数实际上并不执行任何计算。相反,它只是创建并返回一个函数 get () ,该函数在调用 web 页面时获取页面内容。因此,在 get ()中执行的计算实际上被延迟到计算 get ()时程序中的某个后续点。例如:

>>> url1 = page("http://www.google.com") 
>>> url2 = page("http://www.bing.com") 
>>> url1
<function page.<locals>.get at 0x10a6054d0>
>>> url2
<function page.<locals>.get at 0x10a6055f0>
  
>>> gdata = url1()     # Fetches http://www.google.com 
>>> bdata = url2()     # Fetches http://www.bing.com
>>>

可以找到闭包函数中包含的值。

所有函数对象都有一个 _closure_ 属性,如果它是一个闭包函数,那么这个属性将返回一组单元格对象。根据上面的例子,我们知道 url1和 url2是闭包函数。

>>> page.__closure__       # Returns None since not a closure
>>> url1.__closure__
(<cell at 0x10a5f1250: str object at 0x10a5f3120>,)

单元格对象具有存储关闭值的属性 cell_contents。

>>> url1.__closure__[0].cell_contents
'http://www.google.com'
>>> url2.__closure__[0].cell_contents
'http://www.bing.com'

四、总结

当嵌套函数引用其封闭范围中的值时,可以定义 Python 中的闭包。闭包提供了某种形式的数据隐藏。闭包还可以是一种高效的方法,可以在一系列函数调用之间保持状态。用 Python 创建一个闭包函数:

  • 我们必须有一个嵌套的函数
  • 嵌套函数必须引用封闭函数中定义的值
  • 封闭函数必须返回嵌套函数

到此这篇关于如何理解及使用Python闭包的文章就介绍到这了,更多相关Python闭包内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python字符串替换实例分析
May 11 Python
使用Python对IP进行转换的一些操作技巧小结
Nov 09 Python
python使用knn实现特征向量分类
Dec 26 Python
python如何制作英文字典
Jun 25 Python
python树的同构学习笔记
Sep 14 Python
Python进程,多进程,获取进程id,给子进程传递参数操作示例
Oct 11 Python
python 实现屏幕录制示例
Dec 23 Python
python计算导数并绘图的实例
Feb 29 Python
Python3 socket即时通讯脚本实现代码实例(threading多线程)
Jun 01 Python
Python Mock模块原理及使用方法详解
Jul 07 Python
python3.7调试的实例方法
Jul 21 Python
Pytorch自定义Dataset和DataLoader去除不存在和空数据的操作
Mar 03 Python
python pygame入门教程
python plt.plot bar 如何设置绘图尺寸大小
python用tkinter开发的扫雷游戏
Pytorch GPU内存占用很高,但是利用率很低如何解决
Python爬取英雄联盟MSI直播间弹幕并生成词云图
如何判断pytorch是否支持GPU加速
Jun 01 #Python
pytorch 两个GPU同时训练的解决方案
Jun 01 #Python
You might like
PHPCMS的使用小结
2010/09/20 PHP
Zend Framework教程之Zend_Controller_Plugin插件用法详解
2016/03/07 PHP
yii2高级应用之自定义组件实现全局使用图片上传功能的方法
2016/10/08 PHP
PHP封装函数实现生成随机的字符串验证码
2017/01/24 PHP
PHP构造二叉树算法示例
2017/06/21 PHP
PHP常用函数之base64图片上传功能详解
2019/10/21 PHP
PHP基于ip2long实现IP转换整形
2020/12/11 PHP
jquery+ashx无刷新GridView数据显示插件(实现分页、排序、过滤功能)
2010/04/25 Javascript
JQuery实现倒计时按钮具体方法
2013/11/14 Javascript
基于jquery实现等比缩放图片
2014/12/03 Javascript
两种方法解决javascript url post 特殊字符转义 + &amp; #
2016/04/13 Javascript
使用jQuery.Qrcode插件在客户端动态生成二维码并添加自定义Logo
2016/09/01 Javascript
详解ECharts使用心得总结
2016/12/06 Javascript
基于jstree使用AJAX请求获取数据形成树
2017/08/29 Javascript
chorme 浏览器记住密码后input黄色背景处理方法(两种)
2017/11/22 Javascript
vue项目关闭eslint校验
2018/03/21 Javascript
Vue实现移动端页面切换效果【推荐】
2018/11/13 Javascript
基于vue如何发布一个npm包的方法步骤
2019/05/15 Javascript
Vue通过for循环随机生成不同的颜色或随机数的实例
2019/11/09 Javascript
vue 路由懒加载中给 Webpack Chunks 命名的方法
2020/04/24 Javascript
[01:14:10]2014 DOTA2国际邀请赛中国区预选赛 SPD-GAMING VS Orenda
2014/05/22 DOTA
[03:12]完美世界DOTA2联赛PWL DAY6集锦
2020/11/05 DOTA
python3使用urllib模块制作网络爬虫
2016/04/08 Python
Python实现完整的事务操作示例
2017/06/20 Python
Python编程实战之Oracle数据库操作示例
2017/06/21 Python
python爬虫爬取网页数据并解析数据
2020/09/18 Python
css3动画过渡实现鼠标跟随导航效果
2018/02/08 HTML / CSS
多视角3D可旋转的HTML5 Logo动画
2016/03/02 HTML / CSS
18岁生日感言
2014/01/12 职场文书
大学生职业生涯规划范文
2014/01/22 职场文书
社区巾帼文明岗事迹材料
2014/06/03 职场文书
后勤管理员岗位职责
2014/08/27 职场文书
招商引资工作汇报材料
2014/10/28 职场文书
2015年敬老月活动总结
2015/03/27 职场文书
学会感恩主题班会
2015/08/12 职场文书
读《解忧杂货店》有感:请相信一切都是最好的安排
2019/11/07 职场文书