洗礼灵魂,修炼python(23)--自定义函数(4)—闭包进阶问题—>报错UnboundLocalError: local variable 'x' referenced before assignment
闭包(lexical closure)
什么是闭包前面已经说过了,但是由于遗留问题,所以单独作为一个章节详解讲解下
不多说,看例子:
def funx(x):
def funy(y):
return x*y #此时的funy函数对外层funx函数的变量调用,则称为闭包
return funy
结果:
>>> i=funx(4) >>> i <function funx.<locals>.funy at 0x000000000331B7B8> >>> i(5) 20 >>> a=funx(4)(5) #切不可写成a=funx(4(5)) >>> a 20
再一例:

但是呢,有个很严重的问题,并不是外层的变量都可以调用的,如果是这样写的话:
def fun1():
x=5
def fun2():
x*=x
return x
return fun2
结果:

问题来了:UnboundLocalError: local variable 'x' referenced before assignment
这是为嘛啊?看报错信息,提示的是本地变量x再调用前没有被定义,也就是说,fun2函数用的x并不是我们以为的fun1的变量,因为这里写法和上面的例子是不同的,那我改成一样的写法看看:

还是不行,为嘛啊?
我个人的理解是:在之前使用闭包是没有对传入的值进行改动的,是直接返回出来的,当我们传入4或者5时,传入这个举动就已经赋值定义好了X和y,这都是前一章参数的功劳。而后面的写法都是对传入的值有改变(x*=x),因为x指向的是一个不可变对象—常量,我们对传入的值已经改变,也就是内存ID发生改变,那么这个被改过的x*=3得到的X则是一个新的变量,在fun2函数空间内来说,这个X是没有被定义的,所以报错。而在之前我们只是声明了一个原始X的变量,所以返回时不报错
官方解释是:由于python对变量的搜索机制引起,当在调用函数时,如果有一个函数在内部对外层函数的变量做赋值操作 ,该变量会被认为是本地的(这就是前面说的屏蔽),所以如果你修改变量的值就会把其变成局部变量,在修改的同时就会创建一个同名的局部变量来屏蔽,那么该变量的引用自然就会出现未定义,所以这就解释为何直接调用不报错,而赋值操作就会报错
图解(个人理解):

那么这种情况怎么解决呢?在以后的开发中,总有要重新赋值的操作吧?是的,解决方法是有的,而且是多个方法:
方法一:使用nonlocal关键词

那么再直接以原写法加nonlocal关键词看看:

果然可行
注意:nonlocal参数在python3中才有,python2里没有
方法二:使用global关键词
使用global参数有两种情况:
1.已经有一个全局变量被定义,并且希望调用全局变量

2.函数内外层都把对象定义为全局变量,并且希望调用外层函数的变量

方法三:很“猥琐”的一招:建一个新的本地变量不要改动上层函数的变量

是的,很猥琐,基本和外层函数没关系了,只不过值是一样的而已。顺便一说,虽然猥琐,但是确实是个办法,这样的操作在类定义里也会用到
方法四:使用容器类型
容器类型:如果某个对象包含对其他对象的引用,则将其称为容器。
容器不是存放在栈(后进先出的则为栈)里面,不受屏蔽影响,变量不会被屏蔽掉。字符串,列表,元祖都是容器类型
好,那么我们试一下看看:
代码:
def fun1():
x=[5,6]
def fun2():
x[0]*=x[0]
return x[0]
return fun2
结果是可行:

那么这个方法就会带来一个新的问题,先看看例子再说:

x并没有变对不对,这符合在前面说的,字符串,整形(常量)都是不可变对象,所以需要改则得重新赋值,这里要说明一下,变量本身是不可变对象,因为变量的赋值只是贴的一个标签而已,在前面的章节里说过的
那么使用容器类型呢?

果然是变了,因为list本身就是一个可变对象。但是说个很严重的问题,假如你在实际开发中,你的数据是list,这个数据很重要,你这样调用一次,list里的数据就变了,那么如果操作错或者说如果在测试中做测试,那岂不用一次少一次?这个时候,注意,你可以使用列表切片或者拷贝的方法来避免这种事发生
最后做个总结:
- 内部函数,不修改全局变量可以访问全局变量
- 内部函数,修改同名全局变量,则python会认为它是一个局部变量
- 在内部函数修改同名全局变量并调用变量,则引发UnboundLocalError: local variable 'x' referenced before assignment
看到这里,再结合前面的是不是有点懵X了?哈哈,没关系,多练习,多理解,很简单,你会看懂的
洗礼灵魂,修炼python(23)--自定义函数(4)—闭包进阶问题—>报错UnboundLocalError: local variable 'x' referenced before assignment的更多相关文章
- 解决Python报错:local variable 'xxx' referenced before assignment(引)
解决Python报错:local variable 'xxx' referenced before assignment(引) 解决Python报错:local variable 'xxx' refe ...
- [合集]解决Python报错:local variable 'xxx' referenced before assignment
a = 1 def use(): print(a) #输出1 引用不会报错 a = 1 def use(): a = 3 print(a) #输出 3 重新赋值也不会报错. 局部变量会优先在函数内部去 ...
- _markupbase.py if not match: UnboundLocalError: local variable 'match' referenced before assignment,分析Python 库 html.parser 中存在的一个解析BUG
BUG触发时的完整报错内容(本地无关路径用已经用 **** 隐去): **************\lib\site-packages\bs4\builder\_htmlparser.py:78: U ...
- python:UnboundLocalError: local variable 'xxx' referenced before assignment
近来一直都在学习python语言,偶然在伯乐在线看到2017年京东C/C++的面试题.就打算用python+ST3 IDE顺便敲下面试题代码. 原题 C语言: #include <stdio.h ...
- UnboundLocalError: local variable 'foo' referenced before assignment Python常见错误
在定义局部变量前在函数中使用局部变量(此时有与局部变量同名的全局变量存在) 在函数中使用局部变来那个而同时又存在同名全局变量时是很复杂的, 使用规则:如果在函数中定义了任何东西,如果它只是在函数中使用 ...
- python: local variable 'xxx' referenced before assignment
问题发现 xxx = 23 def PrintFileName(strFileName): if xxx == 23: print strFileName xxx = 24 PrintFileName ...
- Python问题:UnboundLocalError: local variable 'xxx' referenced before assignment
参考链接: http://blog.csdn.net/onlyanyz/article/details/45009697 https://www.cnblogs.com/fendou-999/p/38 ...
- python 使用嵌套函数报local variable xxx referenced before assignment或者 local variable XXX defined in enclosing scope
情况一: a 直接引用外部的,正常运行 def toplevel(): a = 5 def nested(): print(a + 2) # theres no local variable a so ...
- Python UnboundLocalError: local variable 'xxx' referenced before assignment 解决方法
一.报错含义: val=9 def test(): print(val) val = 6 print(val) test() 翻译:本地变量xxx引用前没有定义. 二.报错原因 这是Python变量作 ...
随机推荐
- (转)内核模块操作命令-lsmod+rmmod+modinfo+modprobe
原文:http://watchmen.xin/2018/07/13/IT%E7%A7%91%E5%AD%A6%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86%E4%BD%93% ...
- C#开源项目大全
C#开源项目大全 商业协作和项目管理平台-TeamLab 网络视频会议软件-VMukti 驰骋工作流程引擎-ccflow [免费]正则表达式测试工具-Regex-Tester Windows-Ph ...
- 无源码调试smali
0x01 工具①Android Studio最新版.(用的1.5)②apktool尽量使用最新版的.(反编译本人用baksmali-2.1.3.jar)③smalidea插件.下载地址https ...
- 【JAVA】ThreadLocal源码分析
ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...
- java远程调试(idea)
遇见一个怪异问题,无奈线上数据库有限制,只能远程调试下代码.突然发现,远程调试代码真的好简单,简单记录下操作步骤. 1.在idea里创建一个Remote,远程连接的入口. 找到 Edit Config ...
- 【杂谈】对IO与NIO的认识
IO流与NIO块的数据缓存 Java的IO是面向流设计的,通常我们通过IO流读取数据,只能指定读取数据的大小,而不能选择数据读取的起始位置.数据就像流水一样,流过我们的应用,一旦流过就无法回头.除非我 ...
- MRTG在Windows平台的安装及使用
MRTG (Multi Router Traffic Grapher)是一款监控网络流量负载的免费软件,目前利用MRTG已经开发出了各式各样的统计系统: 1.系统资源负载统计,例如:磁盘空间.CPU负 ...
- JQ的offset().top与js的offsetTop区别详解
一.前言 最近在做一个图片懒加载的插件,就纵轴(Y轴)而言,我需要时时获取图片的上偏移量,好判断是否已进入视图区域,而我所理解的是offsetTop应该是跟offset().top一样的,然后陷入了因 ...
- 正则表达式之javascript
1.正则表达式的定义 描述字符模式的对象,JavaScript的RepExp类表示正则表达式 var pattern = new RegExp("s\("); <=> ...
- 数据分析之numpy模块
numpy(numerical python)是python语言的一个扩展程序库,支持大量的维度数组和矩阵运算,此外也针对数组提供大量的数学函数库. 一.创建数组 1 使用array()创建 impo ...