python闭包与引用以及需要注意的陷阱

 更新时间:2020年09月18日 08:28:37   作者:鼠与我  
这篇文章主要介绍了python闭包与引用以及需要注意的陷阱,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下

python闭包

关于闭包, 很多blog中都这样解释 :对于一个嵌套定义的函数,外层的函数的返回值是内层函数,而在内层函数中又引用了外层函数的局部变量,在外层函数执行后,其局部变量并非被回收,而会同返回的内层函数一同存在,而这一现象被称为闭包(closure)。

不过以上的理解有些繁琐和局限, 在计算机科学中 ,闭包(Closure)词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。 这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。 也即对于第一段中的定义可以适当放开一些限制条件,python中的闭包实现也并非那么局限。

引用

通过上文介绍可以对于python闭包有大概的了解, 但是有些看似简单的细节却需要进一步阐述 。

python中变量的概念,这是与C/C++中极为不同的,在C/C++中变量是一个名称与内存合一的实体,改变一个变量的值,并不改变其内存的地址。 而变量这个概念在python中并不合用,很多场合它的运用都会让人混淆 。

python中所使用的概念是引用和对象,即如a=123,a即是一个引用名称,123是内存中所储存的对象值。这其实更像是C/C++中的指针与其所指向的内存,可以看作python在此之上对语法进行了包装。

回到之前讨论的闭包话题,在其中用到了 变量 的概念,即函数引用的 变量 将与函数一同存在,这里的 变量 其实是引用名称与内存对象的复合概念。我们这里对其进行进一步的阐明:

函数中所使用的外层函数引用名称(指针),在外层函数退出后其所指向的内存对象并不回收,而该引用名称(指针)会与内层函数一同存在,虽然此时该引用名称(指针)对于内层函数不是“可见的”。

陷阱

def count(): 
  fs = [] 
  for i in range(1, 4): 
    def f(): 
      return j*j 
    fs.append(f)
  return fs

f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())

对于以上代码,假如按照C/C++中的概念去理解python中的变量,就会以为其输出依次为1、2、3。其实不然,真正输出为:3、3、3。根据上一小节中对于python中引用与闭包的阐述,在内存f函数中使用外层的引用名称i,在循环中虽然将不同的f函数加入到列表fs中,但是它们都使用的是同一个引用i,而该引用最后对应的值为3。

再看一段代码,这个会稍微复杂一点

def test():
  for i in range(4):
    yield i
    
g=test()

for n in [1,10]:
  g=(n+i for i in g)
  
print(list(g))

上面这段代码的输出,一时不查之下也会以为是11、12、13、14,而其真实结果却是20、21、22、23,让人一时抓不到头脑。首先在for循环中的生成器表达式(n+i for i in g),它其实本质上是一个函数,写成表达式的形式不过是一种语法糖,其函数形式为:

def gen(n):
  # g是外面全局的那个生成器g
  for i in g:
    yield n+i

即生成器generator本身是一种算法或是函数,只有在“调用”它的时候,也就是对其进行for或是list或是next之类的操作时,才会真正的有值流动。

那么对于以上第二例子中的代码,在for循环内n=1时,g这个生成器被重新赋值,但注意它此时只是一个特殊的函数,此时的n与i并没有真正相加,在for循环的第二轮n=10的时候,(n+i for i in g)表达式中对g才进行了调用,那么此时流进函数的n值其实是10,也就是此时g这个生成器对应的值为10、11、12、13,也就是i所引用的是这些值,下面又以相同的n+i的形式创造一个新的生成器对g重新赋值,并退出循环。则自然,此时g中对应的值为20、21、22、23.

以上就是python闭包与引用以及需要注意的陷阱的详细内容,更多关于python 闭包与引用的资料请关注脚本之家其它相关文章!

相关文章

  • Python嵌套式数据结构实例浅析

    Python嵌套式数据结构实例浅析

    这篇文章主要介绍了Python嵌套式数据结构,结合实例形式简单分析了Python字典与列表元素的嵌套存储相关定义与操作技巧,需要的朋友可以参考下
    2019-03-03
  • python 捕获shell脚本的输出结果实例

    python 捕获shell脚本的输出结果实例

    下面小编就为大家带来一篇python 捕获shell脚本的输出结果实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • python绘制雪景图

    python绘制雪景图

    这篇文章主要为大家详细介绍了python绘制雪景图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • 树莓派4B安装Tensorflow的方法步骤

    树莓派4B安装Tensorflow的方法步骤

    这篇文章主要介绍了树莓派4B安装Tensorflow的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Python使用numpy产生正态分布随机数的向量或矩阵操作示例

    Python使用numpy产生正态分布随机数的向量或矩阵操作示例

    这篇文章主要介绍了Python使用numpy产生正态分布随机数的向量或矩阵操作,简单描述了正态分布的概念并结合实例形式分析了Python使用numpy模块结合matplotlib绘制正态分布曲线图相关操作技巧,需要的朋友可以参考下
    2018-08-08
  • 深入浅析Python中的yield关键字

    深入浅析Python中的yield关键字

    python中有一个非常有用的语法叫做生成器,所利用到的关键字就是yield。接下来脚本之家小编给大家带来了Python中的yield关键字详细解析,感兴趣的朋友参考下吧
    2018-01-01
  • 使用优化器来提升Python程序的执行效率的教程

    使用优化器来提升Python程序的执行效率的教程

    这篇文章主要介绍了使用优化器来提升Python程序的执行效率的教程,包括编写计时器和使用内建的优化器等,需要的朋友可以参考下
    2015-04-04
  • Python中使用遍历在列表中添加字典遇到的坑

    Python中使用遍历在列表中添加字典遇到的坑

    今天小编就为大家分享一篇关于Python中使用遍历在列表中添加字典遇到的坑,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • python turtle工具绘制四叶草的实例分享

    python turtle工具绘制四叶草的实例分享

    在本篇文章里小编给各位整理的是关于python turtle工具绘制四叶草的实例分享,有兴趣的朋友们可以跟着学习下。
    2020-02-02
  • Python实现批量检测HTTP服务的状态

    Python实现批量检测HTTP服务的状态

    本文给大家分享的是一个使用python实现的批量检测web服务可用性的脚本代码,主要功能有测试一组url的可用性(可以包括HTTP状态、响应时间等)并统计出现不可用情况的次数和频率等。
    2016-10-10

最新评论