关于Python中的闭包详解

目录
  • 1、闭包的概念
  • 2、实现一个闭包
  • 3、在闭包中外函数把临时变量绑定给内函数
  • 4、闭包中内函数修改外函数局部变量
  • 5、注意:
  • 6、练习:
  • 总结

1、闭包的概念

请大家跟我理解一下,如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数。闭包: 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

2、实现一个闭包

# 将函数作为返回值返回,也是一种高阶函数
# 这种高阶函数我们也称为叫做闭包,通过闭包可以创建一些只有当前函数能访问的变量
#   可以将一些私有的数据藏到的闭包中

def outer():
    a = 10

    # 函数内部再定义一个函数
    def inner():
        print('我是outer', a)

    # 将内部函数 inner作为返回值返回
    return inner

# r是一个函数对象,是调用fn()后返回的函数对象
# 这个函数实在fn()内部定义,并不是全局函数
# 所以这个函数总是能访问到fn()函数内的变量
# 外函数返回了内函数的引用
fn = outer()

# r()相当于调用了inner()函数
print("outer引用地址:", outer)
print("inner引用地址:", fn)
fn()
"""
输出结果:
outer引用地址: <function outer at 0x0000000002BB5948>
inner引用地址: <function outer.<locals>.inner at 0x0000000002BB58B8>
我是outer 10
"""

说明上述代码:

对于闭包,在外函数outer中 最后return inner,我们在调用外函数 fn = outer() 的时候,outer函数返回了inner函数对象,inner函数对象是一个函数的引用,这个引用被存入了fn对象中。所以接下来我们再进行fn() 的时候,相当于运行了inner函数。

提示:

一个函数,如果函数名后紧跟一对括号,相当于调用这个函数。如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用。

3、在闭包中外函数把临时变量绑定给内函数

按照我们正常的认知,一个函数结束的时候,会把自己的临时变量都释放还给内存,之后变量都不存在了。一般情况下,确实是这样的。但是闭包是一个特别的情况。外部函数发现,自己的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量送给内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量。

在我编写的实例中,我两次调用外部函数outer,分别传入的值是10和20。内部函数只定义了一次,我们发现调用的时候,内部函数是能识别外函数的临时变量是不一样的。

Python中一切都是对象,虽然函数我们只定义了一次,但是外函数在运行的时候,实际上是按照里面代码执行的,外函数里创建了一个函数,我们每次调用外函数,它都创建一个内函数,虽然代码一样,但是却创建了不同的对象,并且把每次传入的临时变量数值绑定给内函数,再把内函数引用返回。

所以我们每次调用外函数,都返回不同的实例对象的引用,他们的功能是一样的,但是它们实际上不是同一个函数对象。

下面示例进行演示:

def outer(num):
    a = num

    # 函数内部再定义一个函数
    def inner():
        print('我是outer', a)

    # 将内部函数 inner作为返回值返回
    return inner

fn1 = outer(10)
fn2 = outer(20)

print("inner引用地址:", fn1)
fn1()
print("inner引用地址:", fn2)
fn2()
"""
输出结果:
inner引用地址: <function outer.<locals>.inner at 0x00000000026B58B8>
我是outer 10
inner引用地址: <function outer.<locals>.inner at 0x00000000026B5828>
我是outer 20
"""
# 注意两个inner的地址不一样,一个是8B8,一个是828。

4、闭包中内函数修改外函数局部变量

在基本的Python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法

global声明全局变量。全局变量是可变类型数据的时候可以修改。

在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候,在Python3中,可以用nonlocal关键字声明一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。

示例:

def outer(num):
    a = num
    b = 10  # a和b都是闭包变量
    print("原始a值为", a)

    # inner内函数
    def inner():
        # 内函数中想修改闭包变量
        # nonlocal关键字声明变量
        nonlocal a
        a += b
        print('我是outer的a', a)

    # 将内部函数 inner作为返回值返回
    return inner

fn1 = outer(10)

fn1()
"""
输出结果:
原始a值为 10
我是outer的a 20
"""

在内函数中,对闭包变量进行了修改,打印出来的结果也确实是修改之后的结果。

5、注意:

还有一点需要注意,闭包变量实际上只有一份,每次调用一份闭包变量。(这个在Python实现的单利模式下来解释更多)

def outer(num):
    a = num
    b = 10  # a和b都是闭包变量
    print("原始a值为", a)

    # inner内函数
    def inner():
        # 内函数中想修改闭包变量
        # nonlocal关键字声明变量
        nonlocal a
        a += b
        print('我是outer的a', a)

    # 将内部函数 inner作为返回值返回
    return inner

fn1 = outer(10)

fn1()
fn1()
fn1()
"""
输出结果:
原始a值为 10
我是outer的a 20
我是outer的a 30
我是outer的a 40
"""

可以看到第二次第二次调用fn1()方法,a的值有增加了10。

6、练习:

# 求多个数的平均值
# nums = [50,30,20,10,77]

# sum()是一个求和函数
# sum()用来求一个列表中所有元素的和
# print(sum(nums)/len(nums))
# 结果:37.4

# 形成闭包的要件
#   ① 函数嵌套
#   ② 将内部函数作为返回值返回
#   ③ 内部函数必须要使用到外部函数的变量

def make_averager():
    # 创建一个列表,用来保存数值
    nums = []

    # 创建一个函数,用来计算平均值
    def averager(n) :
        # 将n添加到列表中
        nums.append(n)
        # 求平均值
        return sum(nums)/len(nums)

    return averager

# 创建对象,现在就是获得了内函数对象的引用
averager = make_averager()

# 调用内涵书对象
# 这里注意,虽然是调用外函数创建的对象,
# 但是获得的是内函数对象的引用,内函数是有形参的,
# 所以averager对象相当于是内函数对象。
# 所以调用内函数就要传递形参。
print(averager(10))
print(averager(20))
print(averager(30))
print(averager(40))

总结

到此这篇关于关于Python中的闭包详解的文章就介绍到这了,更多相关Python闭包内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python总结之闭包和装饰器

    目录 一.装饰器 1. 装饰器的简单介绍 2. 装饰器的解析过程 二.闭包 三.闭包中nonlocal语句的使用 1. 外部变量的引用和改写 2. nolocal的使用及特点 四.闭包与装饰器 五.闭包的作用 六.几个小栗子 栗子1: 栗子2: 栗子3 七.特殊的装饰器 property 装饰器 1. 我们为什么需要用到property 2. 使用Getters和Setters 3. property的作用 4. 小栗子 staticmethod装饰器和classmethod装饰器 step1:

  • Python中的闭包

    目录 1.闭包概念 2.闭包条件 3.闭包完成计数效果 4.闭包的缺点及作用 1.闭包概念 闭包在函数中提出的概念,简单来说就是一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行.这样的一个函数我们称之为闭包.实际上闭包可以看做一种更加广义的函数概念.因为其已经不再是传统意义上定义的函数. 闭包这个概念不仅仅只有在Python中存在,在任何一个编程语言中都几乎存在. 2.闭包条件 闭包的条件: 外部函数中定义了内部函数 外部函数是有返回值 返回值是:内部函数 内部函数还引用

  • python闭包的实例详解

    1.在外部函数中定义内部函数,内部函数包含访问外部函数.即使外部函数的生命周期结束后,内部函数仍然可以访问外部函数变量. 2.外部函数的返回值是内部函数本身. 实例 def outer(): cheer = 'hello ' def inner(name): return cheer + name return inner if __name__ == "__main__": #输出hello kevin print(outer()('kevin')) 知识点扩展: 闭包的概念 我们尝

  • Python闭包技巧介绍

    目录 1.闭包:用函数代替类 2.访问定义在闭包的内的变量 1.闭包:用函数代替类 有时我们会定义只有一个方法(除了__init__()之外)的类,而这种类可以通过使用闭包(closure)来替代.闭包是被外层函数包围的内层函数,它能够获取外层函数范围中的变量(即使外层函数已执行完毕).因此闭包可以保存额外的变量环境,用于在函数调用时使用.考虑下面这个例子,这个类允许用户通过某种模板方案来获取URL. from urllib.request import urlopen class UrlTem

  • 如何理解及使用Python闭包

    一.Python 中的作用域规则和嵌套函数 每当执行一个函数时,就会创建一个新的局部命名空间,它表示包含函数体内分配的函数参数和变量名的局部环境.我们可以将名称空间看作一个字典,其中键是对象名称,值是对象本身. 解析名称时,解释器首先搜索本地命名空间.如果不存在匹配,则搜索全局名称空间,该名称空间是定义函数的模块.如果仍然没有找到匹配项,则在引发 NameError 异常之前最终检查内置名称空间.下图说明了这一点: 让我们考虑下面的例子: age = 27 def birthday(): age

  • 关于Python中的闭包详解

    目录 1.闭包的概念 2.实现一个闭包 3.在闭包中外函数把临时变量绑定给内函数 4.闭包中内函数修改外函数局部变量 5.注意: 6.练习: 总结 1.闭包的概念 请大家跟我理解一下,如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数.闭包: 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用.这样就构成了一个闭包.一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失.但

  • python中random模块详解

    Python中的random模块用于生成随机数,它提供了很多函数.常用函数总结如下: 1. random.random() 用于生成一个0到1的随机浮点数: 0 <= n < 1.0 2. random.seed(n) 用于设定种子值,其中的n可以是任意数字.random.random() 生成随机数时,每一次生成的数都是随机的.但是,使用 random.seed(n) 设定好种子之后,在先调用seed(n)时,使用 random() 生成的随机数将会是同一个. 3. random.unifo

  • Python中字符串切片详解

    目录 1.没有步长的简单切片 2.有步长的切片方式 在python中,我们定义好一个字符串,如下所示. 在python中定义个字符串然后把它赋值给一个变量.我们可以通过下标访问单个的字符,跟所有的语言一样,下标从0开始.这个时候呢,我们可以通过切片的方式来截取出我们定义的字符串的一部分.使用切片的时候我们有两种方式:没有步长的简单切片和有步长的切片方式 1.没有步长的简单切片 语法格式是这样的: 首先定义一格字符串,比如叫s,然后给它赋值 截取字符串中的一部分,我们用的语法是 s[ start:

  • Python中BeautifulSoup模块详解

    目录 前言 安装库 导入库 解析文档示例 提取数据示例 CSS选择器 实例小项目 总结 前言 BeautifulSoup是主要以解析web网页的Python模块,它会提供一些强大的解释器,以解析网页,然后提供一些函数,从页面中提取所需要的数据,目前是Python爬虫中最常用的模块之一. 安装库 在使用前需要安装库,这里建议安装bs4,也就是第四版本,因为根据官方文档第三版的已经停止更新.同时安装lxml解释器 pip3 install bs4 pip3 install lxml 导入库 from

  • python中random随机函数详解

    目录 一.random基础 二.实数分布 2.1 对称分布 2.2 指数分布 2.3 Beta 分布 2.4 Gamma 分布 2.5 高斯分布 2.6 对数正态分布 2.7 正态分布 2.8 冯·米塞斯分布 2.9 帕累托分布 2.10 威布尔分布 总结 加载相关库 import random import seaborn as sns import matplotlib.pyplot as plt # 解决中文不显示的问题 from pylab import mpl mpl.rcParams

  • Python中reduce函数详解

    目录 1 reduce用法 2 reduce与for循环性能对比 reduce函数原本在python2中也是个内置函数,不过在python3中被移到functools模块中. reduce函数先从列表(或序列)中取出2个元素执行指定函数,并将输出结果与第3个元素传入函数,输出结果再与第4个元素传入函数,…,以此类推,直到列表每个元素都取完. 1 reduce用法 对列表元素求和,如果不用reduce,我们一般常用的方法是for循环: def sum_func(arr):     if len(a

  • Python中Threading用法详解

    Python的threading模块松散地基于Java的threading模块.但现在线程没有优先级,没有线程组,不能被销毁.停止.暂停.开始和打断. Java Thread类的静态方法,被移植成了模块方法. main thread: 运行python程序的线程 daemon thread 守护线程,如果守护线程之外的线程都结束了.守护线程也会结束,并强行终止整个程序.不要在守护进程中进行资源相关操作.会导致资源不能正确的释放.在非守护进程中使用Event. Thread 类 (group=No

  • Python 中 Meta Classes详解

    接触过 Django 的同学都应该十分熟悉它的 ORM 系统.对于 python 新手而言,这是一项几乎可以被称作"黑科技"的特性:只要你在models.py中随便定义一个Model的子类,Django 便可以: 获取它的字段定义,并转换成表结构 读取Meta内部类,并转化成相应的配置信息.对于特殊的Model(如abstract.proxy),还要进行相应的转换 为没有定义objects的Model加上一个默认的Manager 开发之余,我也曾脑补过其背后的原理.曾经,我认为是这样的

  • Python 中的 else详解

    我们都知道 Python 中else的基本用法是在条件控制语句中的 if...elif...else...,但是 else 还有两个其它的用途,一是用于循环的结尾,另一个是用在错误处理的 try 中.这原本是 Python 的标准语法,但由于和大部分其它编程语言的习惯不太一样,致使人们有意或无意地忽略了这些用法.另外,对于这些用法是否符合 0×00 The Zen of Python 的原则以及该不该广泛使用也存在很多争议.例如在我看到的两本书里(Effective Python VS Writ

  • Python中for循环详解

    与其它大多数语言一样,Python 也拥有 for 循环.你到现在还未曾看到它们的唯一原因就是,Python 在其它太多的方面表现出色,通常你不需要它们. 其它大多数语言没有像 Python 一样的强大的 list 数据类型,所以你需要亲自做很多事情,指定开始,结束和步长,来定义一定范围的整数或字符或其它可重复的实体.但是在 Python 中,for 循环简单地在一个列表上循环,与 list 解析的工作方式相同. 1. for  循环介绍 复制代码 代码如下: >>> li = ['a'

随机推荐