如何理解及使用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实现多线程抓取知乎用户
Dec 12 Python
python 去除二维数组/二维列表中的重复行方法
Jan 23 Python
pandas去除重复列的实现方法
Jan 29 Python
python打包exe开机自动启动的实例(windows)
Jun 28 Python
使用python进行广告点击率的预测的实现
Jul 04 Python
FFrpc python客户端lib使用解析
Aug 24 Python
Django框架创建项目的方法入门教程
Nov 04 Python
Python基于pygame实现单机版五子棋对战
Dec 26 Python
python之MSE、MAE、RMSE的使用
Feb 24 Python
Pycharm中import torch报错的快速解决方法
Mar 05 Python
详解python logging日志传输
Jul 01 Python
pycharm进入时每次都是insert模式的解决方式
Feb 05 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
基于php实现随机合并数组并排序(原排序)
2015/11/26 PHP
laravel学习教程之关联模型
2016/07/30 PHP
IE innerHTML,outerHTML所引起的问题
2009/06/04 Javascript
javascript 定义初始化数组函数
2009/09/07 Javascript
extjs grid设置某列背景颜色和字体颜色的实现方法
2010/09/06 Javascript
myFocus slide3D v1.1.0 使用方法与下载
2011/01/12 Javascript
通过正则格式化url查询字符串实现代码
2012/12/28 Javascript
鼠标移到div,浮层显示明细,弹出层与div的上边距左边距重合(示例代码)
2013/12/14 Javascript
jquery 操作两个select实现值之间的互相传递
2014/03/07 Javascript
jQuery实现的分子运动小球碰撞效果
2016/01/27 Javascript
JS不用正则验证输入的字符串是否为空(包含空格)的实现代码
2016/06/14 Javascript
浅谈js中对象的使用
2016/08/11 Javascript
用自定义图片代替原生checkbox实现全选,删除以及提交的方法
2016/10/18 Javascript
扩展jquery easyui tree的搜索树节点方法(推荐)
2016/10/28 Javascript
JavaScript中如何使用cookie实现记住密码功能及cookie相关函数介绍
2016/11/10 Javascript
关于vue-router的那些事儿
2018/05/23 Javascript
jQuery提示框插件SweetAlert用法分析
2019/08/05 jQuery
vue-router之解决addRoutes使用遇到的坑
2020/07/19 Javascript
Vue 修改网站图标的方法
2020/12/31 Vue.js
[55:42]VG vs VGJ.T 2018国际邀请赛淘汰赛BO1 8.21
2018/08/22 DOTA
介绍Python中的fabs()方法的使用
2015/05/14 Python
python3实现抓取网页资源的 N 种方法
2017/05/02 Python
Django实现表单验证
2018/09/08 Python
python实现转圈打印矩阵
2019/03/02 Python
python去除删除数据中\u0000\u0001等unicode字符串的代码
2020/03/06 Python
pycharm 2020 1.1的安装流程
2020/09/29 Python
CSS实现聊天气泡效果
2020/04/26 HTML / CSS
澳大利亚领先的在线礼品网站:Gifts Australia
2020/08/15 全球购物
周年庆典邀请函范文
2014/01/24 职场文书
二审答辩状范文
2015/05/22 职场文书
新手必备之MySQL msi版本下载安装图文详细教程
2021/05/21 MySQL
详解NodeJS模块化
2021/06/15 NodeJs
自从在 IDEA 中用了热部署神器 JRebel 之后,开发效率提升了 10(真棒)
2021/06/26 Java/Android
分布式Redis Cluster集群搭建与Redis基本用法
2022/02/24 Redis
MySQL存储过程及语法详解
2022/08/05 MySQL
html解决浏览器记住密码输入框的问题
2023/05/07 HTML / CSS