Python中的UnboundLocalError是什么错误?如何解决?

如果代码报错UnboundLocalError, 大概率犯了以下错误:

money = 10000  # 当前存款def add_money(value):money += valueif __name__ == '__main__':print('当前存款:', money)add_money(1000)print('当前存款:', money)

其中,变量money表示当前存款;函数add_money将变量money加上指定的值value. 对于新手,或许认为两条print语句中的money分别为10000和11000. 但很遗憾,这段代码运行后会报错:

当前存款: 10000
Traceback (most recent call last):
  File “XXX”, line 10, in
    add_money(1000)
  File “XXX”, line 5, in add_money
    money += value
UnboundLocalError: local variable ‘money’ referenced before assignment

可以看到,调用函数add_money后抛出错误UnboundLocalError. 根据报错信息,局部变量(local variable)money在赋值前被引用。那么,什么是局部变量?这个错误是如何产生的?

1. 局部变量与全局变量

在Python中,如果变量名出现在赋值运算**(=)**的左侧,那么就定义了一个变量。例如:

names = ['你好', 1234]money = 11000

分别定义了变量namesmoney.

在函数体内定义的变量是局部变量,反之,在函数体外定义的变量是全局变量。例如:

book_prices = {}def get_book_price(book_name):value = book_prices.get(book_name, 0)return value

以上代码中,变量book_prices在函数体外定义,它是个全局变量;在函数get_book_price内,定义了变量value, 因此它是个局部变量。

但是,如果全局变量和局部变量同名,它们还是同一个变量吗?例如:

weight = 120def print_size(n):weight = 0.2print(weight * n)

可以看到,以上代码分别定义了全局变量weight, 在函数print_size内也定义了同名的局部变量weight. 稍加实验可以发现,这两个变量并不是同一个变量,也就是说,当调用函数print_size后,全局变量weight的值依然是120, 而不是0.2.

Python为何如此设计呢?想一想,如果名字相同的都是同一个变量,当使用别人的代码库时,就要保证自己使用的变量名不能与别人用过的变量名重复,以避免在代码中修改这些变量。不难想象,这样的编程语言并不好用。

2. 问题解答

有了上述准备,现在可以回答一开始那段代码报错的原因了:

money = 10000  # 当前存款def add_money(value):money += value

首先,这段代码定义了全局变量money. 另外,在函数add_money中,语句money += value等价于money = money + value,变量money出现在赋值运算的左侧,因此在函数体内也定义了局部变量money.

小贴士

在Python的函数中,所有出现在赋值运算(=)左侧的变量都被Python视为局部变量,仅在函数内部可用。

现在,在函数add_money中,变量money被识别为局部变量,然而,对于语句money = money + value, 这条语句运行完毕后才会定义变量money, 然而,变量money的创建需要用到money自身,这让我们联想到“祖父悖论”。对于这类错误,Python就会抛出UnboundLocalError.

但是,我们的本意是在函数add_money内修改全局变量money,有没有办法做到这一点?答案是使用关键字global. 通过在函数体内使用global,告诉Python, 此处修改的是全局变量. 上述代码修改如下:

money = 10000  # 当前存款def add_money(value):global moneymoney += value

修改后,代码输出我们期望的结果。

等等,如果止步于此,可能会形成这样一种错误的认识:只要在函数内部使用全局变量,就要使用global. 然而,在函数体内使用全局变量的需求很常见,但global并不常用。上面这句话错在哪儿?请接着往下看。

3. global并不常用

在函数体内,只有当全局变量出现在赋值运算的左侧时,才需要使用global. 例如在一开始的例子中,我们的目的是在函数add_money内修改全局变量money, money出现在赋值运算(+=)的左侧, 此时才需要使用global.

请看这个例子:

money = 10000def print_money():print('当前余额:', money)

以上代码中,调用函数print_money后,会输出10000. 也就是说,函数print_money内使用的是全局变量money. 因为在函数体内,变量money并没有出现在赋值运算的左侧,也就是说,没有在函数体内定义局部变量。

闭包

像Python, JavaScript这样的动态语言(它们允许在函数体内定义函数)都有闭包特性,当函数需要用到一个变量时,会从内而外一层层地查找变量,直到处于最外层的全局变量。

再来看一个例子:

books = ["If give me 3 days' light", "Fluent Python"]def modify_first_book():books[0] = "Keep alive"if __name__ == '__main__':print("修改前:", books)modify_first_book()print("修改后:", books)

调用函数modify_first_book后,可以看到全局变量books的第0项被修改。注意,在这里的函数modify_first_book中,出现在赋值运算左侧的是books[0], 这不是一个变量名,而是对列表books第0项的引用。

为了对比,稍微修改函数modify_first_book:

books = ["If give me 3 days' light", "Fluent Python"]def modify_first_book():books = []if __name__ == '__main__':print("修改前:", books)modify_first_book()print("修改后:", books)

运行以上代码,可以发现全局变量books并未被修改,因为在函数modify_first_book中,出现在赋值运算左侧的是变量名books,因此会创建一个局部变量books.

现在可以回答为什么global并不常用:我们很少在函数体内直接通过赋值运算符修改全局变量,这种情况大多发生于一些基本的类型,例如int, float, str等等。对于list, dict或是用户自定义的类,通常会调用它们的方法,例如list.append, dict.get, 很少通过赋值运算直接修改它们。当然,如果你这么写过,一定遇到过一些“谜之错误”:代码可以运行,但运行结果总是不符合预期。

Python中还有个关键字nonlocal, 用起来和global差不多,在此不再赘述。

需要Python学习资料和python接单技巧的可以添加小助手w信免费获取和问题答疑哦~
在这里插入图片描述

如有侵权,请联系删除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/46758.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DialogFragment 开发手游sdk代替透明的activity

前言 各位同学大家好 有段时间没有给各位更新文章了,最近在写新的项目 之前的手游sdk 都是用透明的activity 效果有缺陷,现在我改成用这个dialogfragment 来实现 , 废话不多说我们正式开始 效果图 : 为什么要使用dialogfragment: 之前开发手游sdk的时候 我这边都是使用透…

【区块链 + 智慧政务】区块链 +ETC 下一代公路联网收费关键技术优化项目 | FISCO BCOS应用案例

2020 年,我国取消省界收费站项目完成后,随着收费模式与收费方式的变化,形成了以门架为计费单元的新收 费体系:按照车辆通行门架数,RSU 天线读取 ETC 卡、电子标签 OBU 或 CPC 卡内标识的车型信息,车型门架计…

ALlegro批量替换封装?

1,此种情况批量修改同名封装,即改前改后的封装名相同 2,首先将改好后的封装放于库路径下 3,place ----update symbols —package symbols ----选择修改的封装名 4,refresh 完成

开源PS2模拟器 PCSX2 2.0版发布 性能与功能全面升级

时隔多年之后,备受玩家喜爱的PS2模拟器PCSX2迎来了重大更新,2.0版本正式发布!此次更新包含了大量改进,几乎涵盖了模拟器各个方面,为玩家带来更流畅、更便捷的游戏体验。 下载地址: https://pcsx2.net/ 界…

Hadoop-29 ZooKeeper集群 Watcher机制 工作原理 与 ZK基本命令 测试集群效果 3台公网云服务器

章节内容 上节我们完成了: ZNode的基本介绍ZNode节点类型的介绍事务ID的介绍ZNode实机测试效果 背景介绍 这里是三台公网云服务器,每台 2C4G,搭建一个Hadoop的学习环境,供我学习。 之前已经在 VM 虚拟机上搭建过一次&#xff…

品牌形象的智能塑造:Kompas.ai如何构建品牌视觉识别

品牌形象是企业在消费者心中构建的独特印象,它对于品牌识别和记忆度至关重要。一个一致且具有辨识度的品牌形象能够帮助企业在激烈的市场竞争中脱颖而出。Kompas.ai,作为一款智能设计工具,正帮助品牌塑造和维护其独特的视觉识别系统。 一致的…

JMeter进行HTTP接口测试的技术要点

参数化 用户定义的变量 用的时候 ${名字} 用户参数 在参数列表中传递 并且也是${} csv数据文件设置 false 不忽略首行 要首行 从第一行读取 true 忽略首行 从第二行开始 请求时的参数设置: 这里的名称是看其接口需要的请求参数的名称 这里的变量名称就是为csv里面…

帮助中心如何提高用户粘性和活跃度?

帮助中心(Help Center)是在产品网站或者产品内部设立的一个功能模块,用于将产品使用上遇到的问题,或者关于产品的所有问题进行汇总,并通过Q&A(问题与解答)的形式展现给用户,帮助…

【linux】服务器重装系统之系统盘写入准备

【linux】服务器重装系统之系统盘写入准备 【创作不易,求点赞关注收藏】😀 文章目录 【linux】服务器重装系统之系统盘写入准备一、前期准备1、准备一个U盘,并进行格式化2、下载UltralSO工具3、下载对应的Ubuntu版本 二、写入操作教程 一、…

论文分享|AAAI2024‘北航|用大语言模型缩小有监督和无监督句子表示学习的差距

先说结论,大语言模型除了作为聊天的Agent,也可以为检索模型生成优质的文本对训练数据,从而做到无监督场景下也能够适用。这里分享一篇AAAI2024的工作,重点探讨如何生成比评估集更困难的训练数据来提升无监督句子表示学习质量&…

Zynq系列FPGA实现SDI编解码转SFP光口传输(光端机),基于GTX高速接口,提供6套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案在Xilinx-Kintex7上的应用 3、详细设计方案设计原理框图输入Sensor之-->OV5640摄像头输入Sensor之-->HDMIVDMA图像缓存RGB转BT1120GTX 解串与串化SMPTE SD/HD/3G SDI IP核BT1120转RGBHDMI输…

Java二十三种设计模式-适配器模式(6/23)

适配器模式:使不兼容的接口协同工作的桥梁 引言 适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间可以一起工作,通过将一个类的接口转换成客户端期望的另一个接口。 在计算机编程中&#x…

C语言 ——— 调试的时候如何查看当前程序的变量信息

目录 调试前/后的调试窗口 ​编辑 调试窗口 --- 监视 调试窗口 --- 内存 调试窗口 --- 调用堆栈 调试前/后的调试窗口 调试前的调试窗口: 调试前的调试窗口是没有显示的,只有在调试的时候才会有相对应的调试窗口 调试后的调试窗口&#xff1a…

【JVM基础01】——介绍-初识JVM运行流程

目录 1- 引言:初识JVM1-1 JVM是什么?(What)1-1-1 概念1-1-2 优点 1-2 为什么学习JVM?(Why) 2- 核心:JVM工作的原理(How)⭐2-1 JVM 的组成部分及工作流程2-2 学习侧重点 3- 小结(知识点大纲):3-1 JVM 组成3…

风险评估:IIS的安全配置,IIS安全基线检查加固

「作者简介」:冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础著作 《网络安全自学教程》,适合基础薄弱的同学系统化的学习网络安全,用最短的时间掌握最核心的技术。 这一章节我们需…

MySQL运维实战之ProxySQL(9.9)proxysql自身高可用

作者:俊达 proxysql作为一个程序,本身也可能出现故障。部署proxysql的服务器也肯能出现故障。高可用架构的一个基本原则是消除单点。 可以在多个节点上部署proxysql,在proxysql之前再加一层负载均衡(如使用LVS或其他技术&#x…

Ubuntu 22.04.4 LTS (linux) 安装certbot 免费ssl证书申请 letsencrypt

1 安装certbot sudo apt update sudo apt-get install certbot 2 申请letsencrypt证书 sudo certbot certonly --webroot -w 网站目录 -d daloradius.域名.com 3 修改nginx 配置ssl 证书 # 配置服务器证书 ssl_certificate /etc/letsencrypt/live/daloradius.域名.com/f…

Redis的热key解决

1、Redis热Key会带来哪些问题 1、流量集中,达到物理网卡上限。 当某一热点 Key 的请求在某一主机上超过该主机网卡上限时,由于流量的过度集中,会导致服务器中其它服务无法进行。 2、请求过多,缓存分片服务被打垮。 如果热点过于…

AI绘画入门实践|Midjourney 提示词的使用技巧

提示词长短 尽可能做到简洁明了。 提示词很短 MJ 出图的随机性更高,创造的内容更有想象力,更适合创意发散的图像生成。 a dog 提示词很长 MJ 出图会更加精准,但描述太过详细,有可能出现AI理解不到位的情况。 越到后面的提示词&…

17-8 向量数据库之野望8 - 7 个主流向量数据库

​​​​​​ 在快速发展的人工智能 (AI)、机器学习 (ML) 和数据工程领域,对高效数据存储和检索系统的需求至关重要。矢量数据库已成为管理这些技术通常依赖的复杂高维数据的关键解决方案。在这里,我们探讨了每个 AI/ML/数据工程师都应该熟悉的七个矢量数据库,重点介绍了它们…