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

Assigning to variable from parent function: “Local variable referenced before assignment”

本问题已经有最佳答案,请猛点这里访问。

对于以下python 2.7代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/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()

我得到以下错误:

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

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

1
2
3
4
5
6
7
funcA
c 0
funcB 0
funcC 2
funcB 4
funcC 6
end

funcB中的+=funcC中的=两种情况下,是否都没有访问c?为什么它不把错误丢给一个而不是另一个呢?

我没有选择使c成为全局变量,然后在funcB中声明global c。不管怎么说,关键不是让funcB中的c递增,而是为什么它会对funcB而不是funcC抛出错误,同时这两个变量都访问局部或全局变量。


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

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

1
2
tmp = c
c = tmp + 3

由于函数中有对c的赋值,所以该函数中c的其他每次出现都将只在funcB的本地范围内查找。这就是您看到错误的原因,您试图访问c以获取+=的当前值,但在本地范围c尚未定义。

在python 3中,您可以通过使用非本地语句来解决这个问题,该语句允许您将变量分配给不在当前范围内,但也不在全局范围内的变量。

您的代码应该是这样的,在funcC的顶部有一个类似的行:

1
2
3
4
   def funcB():
      nonlocal c
      c += 3
      ...

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()

…和输出:

1
2
3
4
5
6
7
funcA
c 0
funcB 3
funcC 5
funcB 8
funcC 5
end


Isn't 'c' being accessed in both cases of '+=' in funcB and '=' in funcC?

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

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

1
2
3
4
5
6
def outer():
    c = [0]
    def inner():
        c[0] = 3
    inner()
    print c[0]

将打印3

编辑:你想把c作为一个参数传递。python 2没有其他方法,afaik,来获得所需的行为。python 3为这些情况引入了nonlocal关键字。


1)在funcb中的+=funcC中的=两种情况下,是否都没有访问c

不,因为c += 3与:

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

2)我没有选择让c成为全局变量,然后在funcB中声明global c

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

1
def funcB():

用:

1
def funcB(c):

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

注:您还应该对funcBfuncC外的funcA进行联合定义。


另一个肮脏的解决方案,但是,它不要求您使C成为全局的。一切都一样,但:

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


试试这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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值,那么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()

这将产生:

1
2
3
4
5
6
7
8
9
funcA
c 0
funcB 3
funcC 5
funcB 8
funcC 5
end

C:\Python26\