【问题标题】:scoping error in recursive closure递归闭包中的范围错误
【发布时间】:2011-01-31 18:42:13
【问题描述】:

为什么会这样:

def function1():                                                                                                             
       a = 10                                                                                                                    
       def function2():
          print a
       function2()

但事实并非如此:

def function1():
    a = 10
    def function2():
        print a
        a -= 1
        if a>0:
           function2()
    function2()

我收到此错误:

UnboundLocalError: local variable 'a' referenced before assignment

【问题讨论】:

    标签: python recursion scope closures


    【解决方案1】:

    该错误似乎不能很好地描述根本问题。 Mike 解释了这些消息,但这并没有解释根本原因。

    实际的问题是,在 python 中,您不能分配给封闭变量。所以在function2中'a'是只读的。当您分配给它时,您会创建一个新变量,正如 Mike 指出的那样,您在写之前先阅读。

    如果你想从内部范围分配给外部变量,你必须像这样作弊:

    def function1():
        al = [10]
        def function2():
            print al[0]
            al[0] -= 1
            if al[0]>0:
               function2()
        function2()
    

    所以 al 是不可变的,但它的内容不是,您可以在不创建新变量的情况下更改它们。

    【讨论】:

    • 的确,这是设计这个函数的关键——你不能分配给非本地范围。 (注意:al可变的;这就是它起作用的原因。)
    • 我认为,为了清楚起见,区分 al 变量和 al 包含的值很重要。它总是回到我的指针,所以让我这么说;您不能使 al 指向一个新列表,但您可以更改 al 指向的列表的内容。 al->[v1, v2, v3] al 不能更改,但 v1、v2 和 v3 可以更改。 Mike 说这使 al 可变是绝对正确的,因为在我们的术语中,al is 是列表而不是指向列表的指针。
    【解决方案2】:

    需要注意的是,这是 Python 中的一个语法故障。 Python 本身(在字节码级别)可以很好地分配给这些变量; 2.x 中根本没有语法来表明您想要这样做。它假定如果您在嵌套级别分配给变量,则意味着它是它的局部变量。

    这是一个巨大的缺点;能够分配给闭包是基本的。我已经多次使用 charlieb 的 hack 解决这个问题。

    Python 3 使用名称很尴尬的“nonlocal”关键字解决了这个问题:

    def function1():
        a = 10
        def function2():
            nonlocal a
            print(a)
            a -= 1
            if a>0:
               function2()
        function2()
    function1()
    

    这种语法只能在 3.x 中使用,这很糟糕;大多数人都停留在 2.x 中,不得不继续使用 hack 来解决这个问题。这非常需要向后移植到 2.x。

    http://www.python.org/dev/peps/pep-3104/

    【讨论】:

      【解决方案3】:

      在非工作 sn-p 中,当您说“a -= 1”时,您分配给a。因此,在function2 内部,a 是该范围的本地,而不是封闭范围。 Python 的闭包是词法的——它不会在function2 的框架中动态查找a,如果它没有被分配,就去function1 的框架中查找它。

      请注意,这根本不依赖于递归或使用闭包。考虑这个函数的例子

      def foo():
          print a
          a = 4
      

      调用它也会给你一个UnboundLocalError。 (如果没有a = 4,它将使用全局a,或者,如果没有,则引发NameError。)因为a 可能在该范围内分配,所以它是本地的。


      如果我在设计这个功能,我可能会使用更像

      的方法
      def function1():
          a = 10
          def function2(a=a):
              print a
              a -= 1
              if a > 0:
                 function2(a)
          function2()
      

      (或者当然是for a in xrange(10, -1, -1): print a ;-))

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-02
        • 1970-01-01
        • 2014-05-31
        • 2011-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-25
        相关资源
        最近更新 更多