python3闭包函数 - vip共享吧
  • 网站首页
  • IT技术笔记
    • Java教程
    • MySql数据库
    • PHP开发
    • Python教程
    • JavaScript
    • SEO优化
    • 常用工具
  • 好资源福利
    • 会员账号共享
  • 网站模板源码
    • 小程序源码
    • 网站源码
  • 共享网络资源
  • 更多功能
    • 留言吐槽
    • 文章归档
    • 我的邻居
    • 史上今日
    • 视频解析
    • 高清壁纸
    • 公告动态
    • 广告合作
    • 关于我们


导航菜单
  • 网站首页
  • IT技术笔记
    • Java教程
    • MySql数据库
    • PHP开发
    • Python教程
    • JavaScript
    • SEO优化
    • 常用工具
  • 好资源福利
    • 会员账号共享
  • 网站模板源码
    • 小程序源码
    • 网站源码
  • 共享网络资源
  • 更多功能
    • 留言吐槽
    • 文章归档
    • 我的邻居
    • 史上今日
    • 视频解析
    • 高清壁纸
    • 公告动态
    • 广告合作
    • 关于我们
Python

python3闭包函数

2024/9/6 韩俊  Python教程 746 0

闭包是函数式编程的一个重要的语法结构,简单来说闭包就是一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行。这样的一个函数我们称之为闭包。实际上闭包可以看做一种更加广义的函数概念。因为其已经不再是传统意义上定义的函数。

闭包并不只是一个 python 中的概念,在函数式编程语言中应用较为广泛。理解 python 中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想。

闭包介绍

不同编程语言实现闭包的方式是不同的,python 中闭包从表现形式上看,如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

def outer(x):
    def inner(y):
        return x + y
    return inner

如上代码所示,inner(y) 就是这个内部函数,对在外部作用域(但不是在全局作用域)的变量进行引用:x 就是被引用的变量,x 在外部作用域 outer 里面,但不在全局作用域里,则这个内部函数 inner 就是一个闭包。

在函数 outer 中定义了一个 inner 函数,inner 函数访问外部函数 outer 的(参数)变量,并且把 inner 函数作为返回值返回给 outer 函数。当我们调用函数的时候,必须调用外部函数,因为内部函数对我们来说是不可见的。

a = outer(150)
print(a(100))

上面的代码中,语句 a=outer(150),outer 返回一个函数(inner)赋值给 a,此时 a 就是函数 inner。然后语句 print(a(100)) 就是调用 inner 函数并传入一个参数 100,此时 a(100) 的值为 250。

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的局部变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

闭包的注意事项

闭包无法修改外部函数的局部变量。

def outer():
    x = 200
    def inner():
        x = 250
        print("inner x:", x)

    print("outer x:", x)  # 调用 inner 函数之前
    inner()
    print("outer x:", x)  # 调用 inner 函数之后

outer()

运行结果:

outer x: 200
inner x: 250
outer x: 200

从结果上,我们可以得知 x 的值并没有被内部函数修改,在调用 inner 函数前后 x 的值没有发生变化。

闭包无法直接访问外部函数的局部变量。

def outer():
    x = 250
    def inner():  # 上面一行的 x 相对 inner 函数来说是函数外的局部变量(非全局变量)
        x += x
        return x
    return inner

outer()()

运行结果报异常:

UnboundLocalError: local variable 'x' referenced before assignment

但是我们可以间接地通过容器类型来解决,因为容器类型不是存放在栈空间的,inner 函数可以访问到。

def outer():
    x = [250]
    def inner():
        x[0] += x[0]
        return x[0]
    return inner

print(outer()())  # 500

同样,我们也可以通过 nonlocal 关键字来解决,该语句显式的指定 x 不是闭包的局部变量。

def outer():
    x = 250
    def inner():
        nonlocal x  # 把 x 声明为非局部变量
        x += x
        return x
    return inner

print(outer()())

循环中不包含域的概念。

还有一个容易产生错误的事例也经常被人在介绍 python 闭包时提起,我一直都没觉得这个错误和闭包有什么太大的关系,但是它倒是的确是在 python 函数式编程是容易犯的一个错误,我在这里也不妨介绍一下。

for i in range(10):
    pass

print(i)  # 9

在程序里面经常会出现这类的循环语句,python 的问题就在于,当循环结束以后,循环体中的临时变量 i 不会销毁,而是继续存在于执行环境中,所以在循环外部 i 的结果为 9。还有一个 python 的现象是,python 的函数只有在执行时,才会去找函数体里的变量的值。

flist = []
for i in range(3):
    def foo(x):
        print(x + i)
    flist.append(foo)

for f in flist:
    f(2)

可能有些人认为这段代码的执行结果应该是 2,3,4。但是实际的结果是 4,4,4。loop 在 python 中是没有域的概念的,flist 在向列表中添加 func 的时候,并没有保存 i 的值,而是当执行 f(2)的时候才去取,这时候循环已经结束,i 的值是 2,所以结果都是 4。

解决以上问题,改写一下函数的定义就可以了。

flist = []
for i in range(3):
    def foo(x, y=i):
        print(x + y)
    flist.append(foo)

for f in flist:
    f(2)

闭包的作用

闭包可以保存当前的运行环境,以一个类似棋盘游戏的例子来说明。假设棋盘大小为 50*50,左上角为坐标系原点(0,0),我需要一个函数,接收 2 个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。这里需要说明的是,每次运动的起点都是上次运动结束的终点。

origin = [0, 0]
legal_x = [0, 50]
legal_y = [0, 50]

def create(pos=origin):
    def player(direction, step):
        '''
        这里应该首先判断参数 direction,step 的合法性
        比如 direction不能斜着走,step不能为负等
        然后还要对新生成的 x,y 坐标的合法性进行判断处理
        这里主要是想介绍闭包,算法我就不详细写了。
        '''

        new_x = pos[0] + direction[0]*step
        new_y = pos[1] + direction[1]*step
        pos[0] = new_x
        pos[1] = new_y

        # 注意!此处不能写成 pos = [new_x, new_y],因为参数变量不能被修改,而 pos[] 是容器类的解决方法
        return pos
    return player

player = create()  # 创建棋子 player,起点为原点
print(player([1, 0], 10))   # 向x轴正方向移动10步
print(player([0, 1], 20))   # 向y轴正方向移动20步
print(player([-1, 0], 10))  # 向x轴负方向移动10步

运行结果:

[10, 0]
[10, 20]
[0, 20]

也就是我们先沿X轴前进了 10,然后沿 Y 轴前进了 20,然后反方向沿X轴退了 10,坐标分别为:[10,0], [10, 20], [0, 20]。

当然,闭包在爬虫以及 web 应用中都有很广泛的应用,并且闭包也是装饰器的基础,这些内容笔者会在后续的文章中分别介绍,这里就不多谈了。理解了本文中的概念,你应该知道的关于闭包的知识也差不多了,请在自己的编程中尽情使用吧。

本节重要知识点

会使用闭包。

注意闭包使用的注意事项。

把闭包适当的应用到编程中。

点赞:0 分享

上一篇
Python3装饰器
下一篇
Python3匿名函数
作者头像 作者名称 作者性别
韩俊

热门推荐

1 Python3的list类型
2 Python3的基本数据类型
3 Python3装饰器
4 Python3字符串
5 Python自定义模块
6 Python中hashlib模块详解

评论列表

取消回复

    •  
      Login

      韩俊

      趁你现在还有时间,尽你自己最大的努力,努力做成你最想做的那件事,成为你最想成为的那种人,过着你最想过的那种生活。这个世界永远比你想的要更精彩,不要败给生活。

      退出登陆
      • 10076文章
      • 455评论
      • 80微语
  • 广告赞助

  • 二零二五年06月
    一二三四五六日
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
  • 分类

    • 网站模板源码
    • IT技术笔记
    • 好资源分享
    • 共享网络资源
  • 最新文章

      • 幼儿端午祝福语简短句
        • 端午简短祝福语送家人
          • 端午文案祝福语简短励志
            • 经典的黄句子
              • 经典黄色搞笑句子
                • 迷雾通:免费使用的VPN软件
                  • 任推邦:打造你的赚钱新途径!
                    • 任推邦是正规平台吗?任推邦邀请码多少?
  • 热门文章

    • 酷狗音乐VIP账号 酷狗音乐会员账号共享2017.01.29更新
    • 芒果tv vip会员账号 芒果tv会员账号共享2017.01.29更新
    • 爱奇艺vip账号 爱奇艺/PPS会员账号共享2016.12.12更新
    • 优酷/土豆vip会员账号 优酷会员账号共享2017.01.29更新
    • 活动:免费获得爱奇艺VIP/PPS会员账号50天以上的使用权!
    • 酷我音乐VIP账号 酷我音乐会员账号共享2016.12.31更新
    • 暴风影音会员账号 暴风影音会员账号共享2016.12.31更新
    • m1905会员账号 m1905会员账号共享2016.10.21更新
  • 最新评论

    • https://www.vipshare8.com/content/templates/meta/Static/images/tx/10.jpg

      你丫的 文章写得太好了 支持下!![F3...

    • https://www.vipshare8.com/content/templates/meta/Static/images/avatar.jpg

      很棒!刚在某网站看到这个,很欣赏,可惜下...

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=3861064027

      呃呃呃 oooo

    • https://www.vipshare8.com/content/uploadfile/202103/thum-490d1614564497.png

      回复了111:根据激活的时间有一年的有效期!

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=792480561

      到2025就到期了吗?

    • https://www.vipshare8.com/content/uploadfile/202103/thum-490d1614564497.png

      [blockquote]打卡时间:16:...

    • https://www.vipshare8.com/content/templates/meta/Static/images/tx/7.jpg

      俊哥,想借用迅雷一用!但需要手机验证!看...

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=3293901900

      感谢大大的分享

    • https://www.vipshare8.com/content/uploadfile/202103/thum-490d1614564497.png

      回复了好奥v:如果没有解析成功,可以切换接口...

    • https://www.vipshare8.com/content/templates/meta/Api/qqtx.php/?qq=131241242441

      现在视频解析网站怎么用不了啦?

  • 网站统计 I 当前在线:386人

    • 本站管理:1位
    • 用户总数:591位
    • 置顶文章:2篇
    • 日志总数:10076篇
    • 微语总数:80条
    • 评论总数:455条
    • 标签总数:83条
    • 页面总数:8页
    • 分类总数:14个
    • 链接总数:16条
    • 运行天数:3700天
    • 最后更新:5月26日
    • 登录
    • 注册
    • 找回
    Copyright © 2025vip共享吧网站地图 网站备案豫ICP备19004194号-1

    免责声明:本站资源仅供用于学习和交流,本站部分素材内容来源于网络,如有侵权/投稿等,请及时联系站长.

    • 首页
    • 秒懂百科 秒懂百科
    • 搜索
    • 史上今日

    大家都在搜

    • 喜马拉雅vip会员共享
    • 返利网
    • 微信小程序
    • 手机赚钱软件
    • 千图网会员账号共享
    • qq音乐vip账号共享
    • PHP
    • 乐视视频
    • 经典句子
    • 腾讯VIP账号共享
    • 迅雷VIP账号共享
    • 优酷vip账号共享
    • 网盘赚钱
    • 爱奇艺vip账号
    • 咪咕会员账号共享
    • 威客平台
    • 
    • 