闭包(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的更多相关文章

  1. 解决Python报错:local variable 'xxx' referenced before assignment(引)

    解决Python报错:local variable 'xxx' referenced before assignment(引) 解决Python报错:local variable 'xxx' refe ...

  2. [合集]解决Python报错:local variable 'xxx' referenced before assignment

    a = 1 def use(): print(a) #输出1 引用不会报错 a = 1 def use(): a = 3 print(a) #输出 3 重新赋值也不会报错. 局部变量会优先在函数内部去 ...

  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 ...

  4. python:UnboundLocalError: local variable 'xxx' referenced before assignment

    近来一直都在学习python语言,偶然在伯乐在线看到2017年京东C/C++的面试题.就打算用python+ST3 IDE顺便敲下面试题代码. 原题 C语言: #include <stdio.h ...

  5. UnboundLocalError: local variable 'foo' referenced before assignment Python常见错误

    在定义局部变量前在函数中使用局部变量(此时有与局部变量同名的全局变量存在) 在函数中使用局部变来那个而同时又存在同名全局变量时是很复杂的, 使用规则:如果在函数中定义了任何东西,如果它只是在函数中使用 ...

  6. python: local variable 'xxx' referenced before assignment

    问题发现 xxx = 23 def PrintFileName(strFileName): if xxx == 23: print strFileName xxx = 24 PrintFileName ...

  7. Python问题:UnboundLocalError: local variable 'xxx' referenced before assignment

    参考链接: http://blog.csdn.net/onlyanyz/article/details/45009697 https://www.cnblogs.com/fendou-999/p/38 ...

  8. 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 ...

  9. Python UnboundLocalError: local variable 'xxx' referenced before assignment 解决方法

    一.报错含义: val=9 def test(): print(val) val = 6 print(val) test() 翻译:本地变量xxx引用前没有定义. 二.报错原因 这是Python变量作 ...

随机推荐

  1. Ubuntu 16.04安装sogou 拼音输入法

    一.更换为国内的软件源 安装搜狗输入法之前请先更换为国内的软件源,否则无法解决依赖问题.首先,用以下命令打开源列表: sudo gedit /etc/apt/sources.list #用文本编辑器打 ...

  2. 关于jquery中prev()和next()的用法

    用prev()和next()方法动态的添加class.以达到当前元素的前面几个元素或后面的几个元素添加class <body> <ul> <li>1</li& ...

  3. 关于小窗滑动,父级body也跟随滑动的解决方案(2)

    当第一次写这个问题的时候,并不知道竟然还会写2,而且(1)也并没有解决问题. 也发现,这个问题,真实也困住了很多人,找到了张鑫旭(http://www.zhangxinxu.com/wordpress ...

  4. Rsync文件同步工具

    前段时间因公司需求,需要把备份的文件进行同步保存,后面就想到了我们大家都最熟悉的文件同步工作Rsync,于是就捣鼓了一下午时间,然后总结了下大概过程和参数详情. 首先了解了下rsync同步的大致原理: ...

  5. python3中的range函数返回的是列表吗?

    注意,这里说的Python3里面的range函数,和Python2是不同的,返回的不是列表,是可迭代对象. 在python3中,如果执行下面的语句 print(range(10)) 得到结果是 ran ...

  6. Java 范例 - 字节处理

    前言 Java 编程中常会遇到需要进行字节处理的地方,本篇文章就来探讨编程中会遇到的字节处理问题. 字节序 字节序(endianness)是对于多字节数据来说的,它描述了多字节数据存储的顺序,分为大端 ...

  7. android开发(2):多页面的实现 | Fragment的创建与使用

    APP中出现多个页面再常见不过了.使用activity与fragment都能实现多页面,这里使用fragment来实现.延续“知音”这个APP的开发,之前已经创建了底部导航条与mainactivity ...

  8. 全文搜索引擎 Elasticsearch 入门

    1. 百科 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作 ...

  9. JavaWeb学习 (十)————Cookie

    一.会话的概念 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾 ...

  10. HashMap源码解读(JDK1.7)

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...