谁能帮我理解Python变量范围?

时间:2011-08-12 22:49:48

标签: python function

我写了一个看起来像这样的测试程序:

#!/usr/bin/python

def incrementc():
    c = c + 1

def main():
    c = 5
    incrementc()

main()

print c

我认为,因为我在main的主体内调用了incrementc,所以main中的所有变量都会传递给incrementc。但是当我运行这个程序时,我得到了

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    main()
  File "test.py", line 8, in main
    incrementc()
  File "test.py", line 4, in incrementc
    c = c + 1
UnboundLocalError: local variable 'c' referenced before assignment

为什么c不通过?如果我想让一个变量被多个函数引用,我是否必须全局声明它?我在某处看到全局变量很糟糕。

谢谢!

4 个答案:

答案 0 :(得分:6)

你在考虑dynamic scoping。动态范围的问题是incrementc的行为将取决于先前的函数调用,这使得很难推断代码。相反,大多数编程语言(也使用Python)使用静态作用域:c仅在main内可见。

要完成您想要的任务,您可以使用全局变量,或者更好地传递c作为参数。现在,因为Python中的原语是不可变的,所以传递一个整数是不能改变的(它实际上是通过值传递的),因此你必须将它打包到一个容器中,就像列表一样。像这样:

def increment(l):
    l[0] = l[0] + 1

def main():
    c = [5]
    increment(c)
    print c[0]

main()

或者,甚至更简单:

def increment(l):
    return l + 1

def main():
    c = 5
    print increment(c)

main()

通常,全局变量很糟糕,因为它们使得编写难以理解的代码变得非常容易。如果你只有这两个函数,你可以继续使c全局化,因为代码的作用仍然很明显。如果你有更多的代码,最好将变量作为参数传递;通过这种方式,您可以更轻松地查看谁依赖于全局变量。

答案 1 :(得分:3)

当一个变量被赋值给一个作用域时,Python认为它是整个作用域的本地变量,除非你另有说明。

因此,为了让您按照自己的想法行事,您需要使用两个global语句:

#!/usr/bin/python
def incrementc():
    global c
    c = c + 1
def main():
    global c
    c = 5
    incrementc()
main()
print c

否则,你在谈论两种情况下名为c的局部变量。

解决此问题的正常方法是,涉及global

#!/usr/bin/python
def incrementc(c):
    c = c + 1
    return c
def main():
    c = 5
    c = incrementc(c)
    return c
c = main()
print c

此处,在每个函数和全局范围中,c引用不同的变量,您将其作为参数传递并返回值。如果您想只有一个c ,请使用类:

class Foo:
    def __init__(self, c):
        self.c = c
        self.incrementc()
    def incrementc(self):
        self.c = self.c + 1


foo = Foo(5)
print foo.c

答案 2 :(得分:2)

变量c未通过,因为您没有将对c的任何引用传递给函数incrementc。

您在这里看到的是3个范围,全局范围以及函数main和incrementc中的范围。在main中你已经正确定义了一个变量c,但是增量c不知道这个 - 所以尝试增加它会失败。即使这两个函数成功,尝试打印c也会在全局范围内失败,因为它不知道你在main中定义的c。

您有几个选择。一种方法:

def incrementc(c):
    c = c + 1
    return c

def main():
    c = 5
    c = incrementc(c)
    return c

c = main()
print c

注意c是如何被传递的。当然,名称不必保留,你很可能这样写:

def increment(z):
    z = z + 1
    return z

def main():
    bar = 5
    bar = increment(bar)
    return bar

foo = main()
print foo

许多人可能不喜欢的另一个选择(有充分理由)是使用全局变量。在那种情况下:

def incrementc():
    global c # indicate intention to write to this global, not just read it
    c = c + 1 

def main():
    global c # declares c in global space
    c = 5
    incrementc()

main()
print c

您希望修改c的GLOBAL实例的任何函数,您需要通知该函数。所以你说'全球c'。您可以在不执行此操作的情况下从全局读取。如果您决定在函数的本地空间中使用一个值,这将确保(在某种程度上)您不会出错并用类似的名称无意中覆盖全局空间中的值。

希望这很清楚,但随时可以要求澄清任何一点(如果我错误地描述了这一点,我也愿意纠正。)

答案 3 :(得分:-1)

全局变量很糟糕。

就像朋友和敌人一样。让你的朋友保持亲密,但让你的爱人更近。

函数main最后一个局部变量c,赋值为5 然后调用函数inc..C。 main中的c现在已超出范围,因此您尝试使用不在范围内的c值 - 因此出错。