从父函数赋值给变量:“赋值前引用的局部变量”

时间:2012-01-19 23:13:15

标签: python

对于以下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

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

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

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

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

5 个答案:

答案 0 :(得分:63)

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

这意味着如果对函数内部的名称进行了任何赋值,则在访问名称之前必须已在最内层作用域中定义该名称(除非使用了全局语句)。在您的代码中,行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

答案 1 :(得分:6)

  

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关键字。

答案 2 :(得分:2)

1)funcB中的c+=中的=两种情况都未访问funcC

不,因为c += 3与:

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

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

请不要那样做,只需更改:

def funcB():

使用:

def funcB(c):

稍后在您的代码中致电funcB(c)

注意:您还应该使用cosider来定义funcB之外的funcCfuncA

答案 3 :(得分:0)

试试这个:

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\

答案 4 :(得分:0)

另一个肮脏的解决方法,但是,它不要求你使c全局。一切都一样,但是:

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