V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
eriale
V2EX  ›  问与答

在 Python 的闭包中为什么不能改变被捕获变量?

  •  
  •   eriale · 2014-12-25 18:00:07 +08:00 · 4019 次点击
    这是一个创建于 3645 天前的主题,其中的信息可能已经有所发展或是发生改变。
    def fn(n):
        def _inner(i):
            n += i
            return n
        return _inner
    

    返回的错误是:

    UnboundLocalError: local variable 'n' referenced before assignment

    我看了SO上的提问这篇文章, 在python2用一个容器包裹n后可以修改,如果在Python3可以通过声明nonlocal来解决这个问题。

    因为JS里面可以直接对n修改,我想问的是Python对闭包中变量的处理跟Javascript中有什么不同?

    14 条回复    2014-12-26 17:38:33 +08:00
    rcmerci
        1
    rcmerci  
       2014-12-25 18:13:38 +08:00
    python2里面就是这样的。
    好像python3改了(没用过3不确定)
    所以我更喜欢js
    clino
        2
    clino  
       2014-12-25 19:06:49 +08:00 via Android
    问题出在 n += i 上,关键是有赋值,python 认为这里声明了一个局部变量
    换个变量名就行
    clino
        3
    clino  
       2014-12-25 19:07:59 +08:00 via Android
    def _inner(i):
    j = n + i
    return j
    yjfuk
        4
    yjfuk  
       2014-12-25 19:08:41 +08:00
    ffffwh
        5
    ffffwh  
       2014-12-25 19:12:25 +08:00 via Android
    闭包也是函数式编程推广开来的概念,函数式编程一般要无副作用。在有副作用的环境下闭包该是个什么套路我是没想清楚的。
    tombkeeper
        6
    tombkeeper  
       2014-12-25 19:29:19 +08:00
    use nonlocal
    sujin190
        7
    sujin190  
       2014-12-25 20:23:22 +08:00
    里边用global n声明就行了
    sujin190
        8
    sujin190  
       2014-12-25 20:28:03 +08:00
    好吧,还没注意过这样不行,直觉按python的设计应该是可以的,原来不行啊
    clino
        9
    clino  
       2014-12-25 21:55:54 +08:00
    @sujin190 你有试过global可以吗?
    sujin190
        10
    sujin190  
       2014-12-25 22:42:02 +08:00
    @clino 试过了,不行
    bottleimp
        11
    bottleimp  
       2014-12-26 09:58:34 +08:00
    lz 你要用闭包, 你就得有 immutable 的思想.
    eriale
        12
    eriale  
    OP
       2014-12-26 10:53:37 +08:00
    @yjfuk 原来的代码是要在n上累加,你的代码是不能累加的。
    eriale
        13
    eriale  
    OP
       2014-12-26 10:57:32 +08:00
    @bottleimp 闭包不一定要immutable吧,这个例子是在<黑客和画家>看到的,Ruby/JS是可以直接修改被捕获的变量,我就想知道Python对闭包中变量的处理有哪些不一样?更进一步,Python这么设计有什么考虑吗?
    bottleimp
        14
    bottleimp  
       2014-12-26 17:38:33 +08:00
    @eriale 我对 ruby 还有 JS 不熟, 单纯的从 python 的角度看, 我觉得这个是跟语言具体实现时候有关的.
    一个函数, 背后实际应该是由一个环境(env) 跟一个函数来组成的. python 中对这个 env 里的变量, 还会区分是否 local. 当函数里有赋值的变量, 可以认为打上了 local 标记, 并且 shadow 了env 中原有的同名变量.
    所以在使用 n = n + 1 这种语句的时候, 一旦 python 需要计算表达式 n+1 的值, 它就会认为这个 n 是 local varible, 并且还没有 bind, 就会抛 UnboundLocalError.

    同样的情况你在最外层的函数也会有, 只是最外层函数我们可以用 global 来限定, 但是你这个是闭包, 相当于中间那层有个变量 n 要在最里层用, 对于这种情况, python2 里面就没有类似 global 这样的对应方法, 在 python3中加了 nonlocal 来处理这种情况.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5378 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:19 · PVG 15:19 · LAX 23:19 · JFK 02:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.