开发者

Can anyone help me understand Python variable scoping?

I wrote a test program that looked like this:

#!/usr/bin/python

def incrementc():
    c = c + 1

def main():
    c = 5开发者_如何学Go
    incrementc()

main()

print c

I'd think that since I called incrementc within the body of main, all variables from main would pass to incrementc. But when I run this program I get

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

Why isn't c passing through? And if I want a variable to be referenced by multiple functions, do I have to declare it globally? I read somewhere that global variables are bad.

Thanks!


You're thinking of dynamic scoping. The problem with dynamic scoping is that the behavior of incrementc would depend on previous function calls, which makes it very difficult to reason about the code. Instead most programming languages (also Python) use static scoping: c is visible only within main.

To accomplish what you want, you'd either use a global variable, or, better, pass c as a parameter. Now, because the primitives in Python are immutable, passing an integer can't be changed (it's effectively passed by value), so you'd have to pack it into a container, like a list. Like this:

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

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

main()

Or, even simpler:

def increment(l):
    return l + 1

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

main()

Generally, global variables are bad because they make it very easy to write code that's hard to understand. If you only have these two functions, you can go ahead and make c global because it's still obvious what the code does. If you have more code, it's better to pass the variables as a parameter instead; this way you can more easily see who depends on the global variable.


When a variable is assigned to in a scope, Python assumes it's local for the whole scope unless you tell it otherwise.

So, to get this to work as you think it will, you need to use two global statements:

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

Otherwise, you're talking about a local variable named c in both situations.

The normal way to solve this, however, does not involve globals.

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

Here, in each function and in the global scope, c refers to a different variable, which you are passing around as an argument and with return values. If you wanted only one c, use a class:

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


The variable c isn't passing through because you do not hand any reference to c to the function incrementc.

What you're looking at here are 3 scopes, the global scope and those within the functions main and incrementc. In main you've properly defined a variable c, but increment c has no knowledge of this - so attempting to increment it is going to fail. Even if those two functions succeeded, trying to print c would fail in the global scope because it has no knowledge of the c you've defined in main.

You have a few options. One way to do this:

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

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

c = main()
print c

Notice how c is being handed around. Of course, the name doesn't have to be preserved, you very well could write it like this:

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

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

foo = main()
print foo

Another option that many would probably dislike (for good reason) is to use globals. In that case:

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

Any function in which you hope to MODIFY the GLOBAL instance of c, you need to inform the function. So you state, 'global c'. You can READ from the global without doing so. This would ensure (to some extent) that you don't make a mistake and overwrite a value in the global space unintentionally with a similar name, should you decide to use one in the local space of a function.

Hopefully that's clear enough, but feel free to ask for clarification on any point (I'm also open to being corrected if I've mistakenly described any part of this).


Global variables are bad.

Just like friends and enemys. Keep your friends close but keep your enemys even closer.

The function main last a local variable c, assignment the value 5 You then call the function inc..C. The c from main is now out of scope so you are trying to use a value of c that is not in scope - hence the error.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜