首页 > 编程语言 >Python中的UnboundLocalError是什么错误?如何解决?

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

时间:2023-11-20 23:13:09浏览次数:38  
标签:name 错误 Python money value UnboundLocalError print 变量 函数

在一个月黑风高的夜晚,我们满心欢喜地写出以下代码:

money = 10000  # 当前的财产,单位为元


def add_money(value):
    money += value


print('当前财产: ', money)
add_money(10)
print('当前财产: ', money)

以上代码中,定义了函数add_money, 将money增加value. 我们期待着运行代码后,程序能够依次输出10000和10010. 可惜事与愿违,运行后程序会报以下错误:

  money += value
     ^^^^^

UnboundLocalError: cannot access local variable 'money' where it is not associated with a value

根据报错信息,money += value这行代码出现了错误,而且报错为UnboundLocalError.:无法访问局部变量money.

那么,什么是局部变量(local variable)呢?为了搞清楚这个问题,我们首先需要了解Python中变量的作用域。

 

1. 变量的作用域

所谓的作用域,指的是可以访问到变量的范围。请看下面这个例子:

def add(a, b):
    c = a + b
    return c


print(c)

这段代码运行时会报错,编译器会提示未定义变量c:因为变量c定义在函数add内部,函数外是访问不到变量c的。因此,可以说变量c的作用域在函数add内部。

 

再来看下面这段代码:

name = '高尔夫的基'


def print_name():
    print(name)


print_name()

这段代码可以正常运行。变量name定义在函数print_name外部,但是函数能访问到这个变量。定义在模块顶层的变量,类似于这里的name,我们称之为全局变量(global variable).

如果你不太明白什么叫定义在模块顶层的变量,可以理解为在函数外定义的变量。

 

2. 局部变量

现在,来看下面这段代码:

name = '高尔夫的基'


def print_name():
    name = '赵奔三'
    print(name)


print_name()
print(name)

上述代码运行后,输出如下:

赵奔三
高尔夫的基

在函数print_name内,将变量name赋值为字符串 '赵奔三' ,因此调用print_name()后,会输出字符串'赵奔三', 这并不奇怪。“奇怪”的是,代码运行到print(name)后,会输出字符串'高尔夫的基', 看起来全局变量name没有受到函数print_name的影响,这是为什么呢?

在Python中,当变量名出现在赋值运算符(也就是=,当然+=, -=这种也算)的左边时,会创建一个新的变量,这样的变量称为局部变量。例如,在函数print_name中,对于name = '赵奔三'这行代码,在=的左边出现了变量名name,Python就会创建一个局部变量name供函数print_name内部使用。

为什么要这么设计呢?大家可以想一想,如果一个变量可以在代码的任意位置被随意修改,随着我们的代码越写越长,如果哪天不小心定义了一个同名的变量,那么这个变量到底代表什么呢?从而,我们的代码会更容易出错。

 

说到这里,我必须强调一点:在函数体内部,当变量名出现在赋值运算符的左边时,才会创建局部变量。请看下面这个例子:

lst = [123, 'abc', 3.45]


def set_lst0(value):
    lst[0] = value


print(lst)
set_lst0('针不戳')
print(lst)

以上代码运行后的输出为:

[123, 'abc', 3.45]
['针不戳', 'abc', 3.45]

这里,函数set_lst0将列表lst(它是个全局变量)的第0项修改为传入的参数value. 可以看到,调用函数set_lst0后,列表lst的第0项成功被修改。究其原因,在函数set_lst中,对于语句lst[0] = value,=的左边lst[0]并不是变量名,因此谈不上创建局部变量,函数内使用的lst是全局变量lst.

 

3. 解决方法

现在再来看看一开始的那行代码money += value,这行代码等价于money = money + value. 

这行代码位于函数add_money内,而且变量money出现在赋值运算符=的左边,因此Python认为money是一个局部变量,从而此函数内的变量money与函数外定义的全局变量money没有半毛钱关系。

然而,在这行代码的右边出现了变量money, 局部变量money被创建前,程序就要读取它的值,这不是耍流氓吗?所以会报错,这种错误就称为UnboundLocalError.

 

但如果我们非要在函数内修改全局变量money呢?很简单,在函数内告诉Python, 我们这里使用的是全局变量money. 怎么告诉Python呢?通过global关键词告诉Python:

money = 10000  # 当前的财产,单位为元


def add_money(value):
    global money
    money += value


print('当前财产: ', money)
add_money(10)
print('当前财产: ', money)

现在运行以上程序,会输出预期的结果。

 

4. 为什么之前没遇到这种问题

很多人困惑的是:自己以前好像从来没有遇到这种情况,怎么学到global这里就突然出现了这种问题?

事实上,我们很少写出本文开始时的那种代码。例如,我们定义了一个全局变量a,它是一个空列表:

a = []

如果你在函数内部修改这个列表,多数情况下你会在函数内通过a.append或者a.remove这些方法修改列表,例如:

def do_sth_a():
    a.append(XXX)
    ...
    a.remove(XXX)

而不会写成:

def do_sth_a():
    a = []

或者:

def do_sth_a():
    a = 123

也就是说,你不大可能直接通过=修改列表a. 当然,如果你这么写过,那么你一定遇到过一些“谜之错误”,代码或许可以运行,但运行结果和你的预期总是不一样。

 

5. 小结

编程专家告诉我们,在代码中慎用全局变量,因此关键字global并不受待见。如果你已经学到了类,那么用到global的概率会进一步降低。只有在极其特殊的几种情况下,global会很有用,不过大多数人在以后的编程生涯中都不会再用到它。我在学习global这个关键字后,也只在教别人的时候用到过

标签:name,错误,Python,money,value,UnboundLocalError,print,变量,函数
From: https://www.cnblogs.com/overxus/p/17681222.html

相关文章

  • Python小白入门指南:避免踩雷的10大错误!
    hello,大家好!新手小白踏入Python的大门有点像冒险,但别担心,我已经整理了一个超实用的入门指南,帮你规避学习过程中的十大雷区。这里有关于Python的错误你应该注意的建议,一起来看看吧!1.拼写错误小心prin和print的奇妙之旅!#错误示例prin("Hello,World!")#建议:尽量保......
  • Python——第三章:函数的定义
    函数:对某一个特定的功能或者代码块进行封装.在需要使用该功能的时候直接调用即可定义:def函数的名字():被封装的功能或者代码块->函数体调用:函数的名字()好处:让程序更加简洁.代码更加合理defbuy_cai():#定义函数print("1.打车")print("2.去菜......
  • mysql 登录错误次数
    如果连续5次输入密码错误,限制登录数据库10分钟1.安装插件(CONNECTION_CONTROL和CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS)installpluginCONNECTION_CONTROLsoname'connection_control.dll'installpluginCONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTSsoname'connection_con......
  • 1688 商品详情 APIERP 选品专用 API 接口 Python Java
    1688商品详情API接口是一种程序化的接口,它允许商家或开发者使用自己的编程技能,对1688平台上的商品信息进行查询、获取和更新。通过这个API接口,商家可以根据自己的需求,获取商品的详细信息,如价格、库存、描述、图片等,从而更好地进行营销和客户服务。使用1688商品详情API接......
  • python的迭代器:如何使用Python迭代器来提高编程效率
    Python的迭代器是一种特殊的对象,它可以用来遍历可迭代对象(如列表、字典、元组)中的元素。它通过实现__iter__()和__next__()方法来实现迭代器功能,并使用next()函数来获取下一个元素。Python的迭代器是一种特殊的对象,它可以用来遍历可迭代对象(如列表、字典、元组)中的元素。它通......
  • python之代理ip的配置与调试
    前言代理IP是网络爬虫中常用的技术手段。通过使用代理服务器,可以实现对特定网站的访问次数限制、避免IP封锁等问题。本文将介绍Python中代理IP的配置与调试方法,并附带代码实例。一、代理IP的配置Python中使用代理IP需要使用requests库,而requests库中则需要设置proxies参数。proxies......
  • Centos7 使用yum从第三方仓库安装Python3.8
    环境:CentOSLinuxrelease7.9.2009起因:Centos7自带Python2.7.5版本。而默认的YUM安装的python3是3.6版本,遂升级到3.8版本。installPython3.8yuminstall-ycentos-release-scl#仓库注册yuminstall-yrh-python38which#安装python3.8#创建软连接ln-s/opt......
  • python处理Excel文件的导入
    处理的文件:label.xlsxAPIDGroupBorrower【HoldingCompanyID】GroupBorrower【TypeofCompany】Watermark3.0(Migrationclientonly)【EffectiveDate】12681974Affiliate 17411268HoldingCompany 18902073Affiliate2023/12/3019552136......
  • python处理数据的导出到Excel
    importdatetimeimportjsonimportosimportpandasaspdfromsqlalchemyimportcreate_enginefromsqlalchemy.sqlimporttext#数据库连接配置,请根据你的实际情况修改db_config={'host':'your_database_host','user':'your_data......
  • python2和3的语法区别
    `` - python2相当于repr  python3不能使用Input-python2如果输入字符需要加双引号,数字不需要加 python3数字字符都可以Raw_input-python2输入数字字符都可以    python3丢弃exceptException,e:- python2可以用  python3语法报错,推荐使用exceptE......