【问题标题】:Assigning to variable from parent function: "Local variable referenced before assignment" [duplicate]从父函数分配给变量:“分配前引用的局部变量”[重复]
【发布时间】:2012-01-19 23:13:14
【问题描述】:

对于以下 Python 2.7 代码:

#!/usr/bin/python

def funcA():
   print "funcA"
   c = 0 
   def funcB():
      c += 3
      print "funcB", c

   def funcC():
      print "funcC", c

   print "c", c
   funcB()
   c += 2
   funcC()
   c += 2
   funcB()
   c += 2
   funcC()
   print "end"

funcA()

我收到以下错误:

File "./a.py", line 9, in funcB
    c += 3
UnboundLocalError: local variable 'c' referenced before assignment

但是当我在funcB 中注释掉c += 3 行时,我得到以下输出:

funcA
c 0
funcB 0
funcC 2
funcB 4
funcC 6
end

在funcB 中的+= 和funcC 中的= 两种情况下都不会访问c 吗?为什么它不会为其中一个抛出错误,而不会为另一个抛出错误?

我无法选择将c 设为全局变量,然后在funcB 中声明global c。无论如何,重点不是让c 在funcB 中增加,而是为什么它会在funcB 而不是funcC 引发错误,而两者都在访问本地或全局变量。

【问题讨论】:

  • 将 c 作为参数传递...
  • 我现在稍微修改了代码,现在是问题的正确版本。
  • 这个链接也有一些信息,docs.python.org/faq/…
  • 移至 python 3 并使用 nonlocal 关键字...
  • @crk - 查看我最近的编辑,这应该有助于澄清。

标签: python


【解决方案1】:

您在这里看到的是访问和分配变量之间的区别。在 Python 2.x 中,您只能分配给最内层范围或全局范围内的变量(后者是通过使用 global 语句完成的)。您可以访问任何封闭范围内的变量,但不能访问封闭范围内的变量,然后在最内层或全局范围内分配给它。

这意味着如果在函数内部对名称进行了任何赋值,则该名称必须在访问该名称之前已经在最内层范围内定义(除非使用了全局语句)。在您的代码中,c += 3 行基本上等同于以下内容:

tmp = c
c = tmp + 3

因为函数中存在对c 的赋值,所以该函数中每隔一个出现的c 只会在funcB 的本地范围内查找。这就是您看到错误的原因,您正在尝试访问 c 以获取 += 的当前值,但尚未在本地范围内定义 c。

在 Python 3 中,您可以使用 nonlocal statement 解决此问题,它允许您分配给不在当前范围内但也不在全局范围内的变量。

您的代码看起来像这样,在funcC 的顶部有类似的一行:

   def funcB():
      nonlocal c
      c += 3
      ...

在 Python 2.x 中,这不是一个选项,您可以更改非局部变量值的唯一方法是它是可变的。

执行此操作的最简单方法是将值包装在一个列表中,然后在您之前刚刚使用变量名的每个位置修改和访问该列表的第一个元素:

def funcA():
   print "funcA"
   c = [0]
   def funcB():
      c[0] += 3
      print "funcB", c[0]

   def funcC():
      c[0] = 5
      print "funcC", c[0]

   print "c", c[0]
   funcB()
   funcC()
   funcB()
   funcC()
   print "end"

funcA()

...和输出:

funcA
c 0
funcB 3
funcC 5
funcB 8
funcC 5
end

【讨论】:

  • 多么好的解释!当我拼命地在其他 SO 答案以及外部互联网。当我遇到一些反例 not 产生该死的错误时,我更加发疯了,并解释说在 Python 中访问全局变量都可以!终于在这里开悟了! ^_^
【解决方案2】:

在 funcB 中的 '+=' 和 funcC 中的 '=' 两种情况下不是都访问了 'c' 吗?

不,funcC 生成一个新变量,也称为 c。 = 在这方面与 += 不同。

要获得您(可能)想要的行为,请将变量包装在一个单元素列表中:

def outer():
    c = [0]
    def inner():
        c[0] = 3
    inner()
    print c[0]

将打印3。

编辑:您需要将c 作为参数传递。 Python 2 没有其他方法(AFAIK)来获得所需的行为。 Python 3 针对这些情况引入了nonlocal 关键字。

【讨论】:

    【解决方案3】:

    1) 在funcB中的+=和funcC中的=这两种情况下,c不是都被访问了吗?

    不,因为c += 3 与:

    c = c + 3
        ^
        |
    and funcB does not know what this c is
    

    2) 我无法选择将 c 设为全局变量,然后在 funcB 中声明 global c。

    请不要那样做,只是改变:

    def funcB():
    

    与:

    def funcB(c):
    

    稍后在您的代码中调用funcB(c)。

    注意:您还应该考虑在funcA之外定义funcB和funcC

    【讨论】:

      【解决方案4】:

      另一个肮脏的解决方法,但是,它不需要您将 c 设为全局。一切都一样,但是:

      def funcB():
          globals()['c'] += 3
          print "funcB", c
      

      【讨论】:

      • 好吧,整个问题的方式并不是很优雅,但只是为了让它发挥作用——这种方法有效。
      【解决方案5】:

      试试这个:

      def funcA():
         print "funcA"
         c = 0
         def funcB(c):
            c += 3
            print "funcB", c
      
         def funcC(c):
            c = 5
            print "funcC", c
      
         print "c", c
         funcB(c)
         funcC(c)
         funcB(c)
         funcC(c)
         print "end"
      
      funcA()
      

      如果你想记住 c 值,那么:

      def funcA():
         print "funcA"
         c = 0
         def funcB(c):
            c += 3
            print "funcB", c
            return c
      
         def funcC(c):
            c = 5
            print "funcC", c
            return c
      
         print "c", c
         c = funcB(c)
         c = funcC(c)
         c = funcB(c)
         c = funcC(c)
         print "end"
      
      funcA()
      

      这将产生:

      funcA
      c 0
      funcB 3
      funcC 5
      funcB 8
      funcC 5
      end
      
      C:\Python26\
      

      【讨论】:

        猜你喜欢
        • 2012-08-07
        • 2013-02-28
        • 2016-04-07
        • 2021-12-16
        • 2012-06-06
        • 2015-06-08
        • 2020-07-08
        相关资源
        最近更新 更多