7

I came across some code which kind of puzzled me. Here's a minimal example which shows this:

# of course, the ... are not part of the actual code
some_var = {"key1":"value1" ... "keyN":"valueN"}

def some_func():
   v = some_var["key1"]

The code works, but the fact that I can access some_var directly confuses me. The last time I had to write some Python code, I remember having to write some_func like this:

def some_func():
   global some_var
   v = some_var["key1"]

I am using Python 2.7.1 on a Windows 7 PC. Did something change in the 2.7 release that allows for this?

Geo
  • 93,257
  • 117
  • 344
  • 520

6 Answers6

6

No, you just can't reassign some_var in a local scope. Consider the following:

some_var = {}
def some_func():
    # some_var[5] = 6
    some_var = {1:2}
    some_var[3] = 4
some_func()
print (repr(some_var)) # {}

You'll see the assignment in some_func actually creates a local variable which shadows the global one. Therefore, uncommenting the line would result in an UnboundLocalError - you can't access variables before they're defined.

Kirk Strauser
  • 30,189
  • 5
  • 49
  • 65
phihag
  • 278,196
  • 72
  • 453
  • 469
  • 2
    That is one of the (very) few things I don't like about Python. – Peter C Mar 03 '11 at 17:21
  • Yeah, but the code only reads data from the dict. Why is it possible without the use of `global`? – Geo Mar 03 '11 at 17:21
  • @Geo: Because assigning and accessing are different things. – user225312 Mar 03 '11 at 17:23
  • @Geo It is because you can access (not assign) any value of an outer scope. That's how scoping in Python works, and if it weren't that way, you'd have to `global` every single variable **and function** you'd want to use inside your function. – phihag Mar 03 '11 at 17:24
  • @Geo: You can reference variables in a larger scope than your current one. You just can't assign to them. You need to use the `global` keyword in that case. – Peter C Mar 03 '11 at 17:25
  • @alpha: It may seem weird, but since you're creating new locals way more often than you're overwrite nonlocals, it's actually a good choice. Assuming the presence of `nonlocal` of course (otherwise closures can get very ugly). Granted, a warning would be nice. But then again, dynamicness doesn't make this easy. –  Mar 03 '11 at 17:28
  • @delnan: I suppose it does make sense, but IMO it's kinda unnecessarily confusing. Also, if you have two variables with the same name in different scopes, one containing the other, usually you want them to reference the same one. – Peter C Mar 03 '11 at 17:33
  • I just realised that the commented out line `# some_var[5] = 6` doesn't work because the local variable created just below shadows the global one _from_ _the_ _beginning_ of the function scope, and not because its bad syntax in itself: you can access the global name, and if points to a mutable object you can change it, you just can't rebind the var name at global scope. So if you comment that line out instead, the commented out one would work. I know, that's what the answer says, but I misunderstood it on first reading, just a friendly warning to others :) – xuloChavez Jan 19 '12 at 20:05
3

You only have to use global if you want to assign a new value to that variable.

Nested scope was introduced in Python 2.1 (and enabled by default in Python 2.2) (emphasis mine):

Put simply, when a given variable name is not assigned a value within a function (by an assignment, or the def, class, or import statements), references to the variable will be looked up in the local namespace of the enclosing scope. A more detailed explanation of the rules, and a dissection of the implementation, can be found in the PEP.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Ah, so only reassigning the variable requires the use of `global`. Everything else is fair game? – Geo Mar 03 '11 at 17:24
  • @Geo No, you can't `del` variables. Basically, you can just read them - but that covers almost anything in Python, including array access, calling a method, calling a method on an object, etc. – phihag Mar 03 '11 at 17:30
3

There's a difference between using (e.g. calling or using in an expression) a name from an outer scope and assigning it (and there's a difference between assigning a bare variable and assigning a member of an object pointed to by a variable - x.y = ... and x[...] = ... count as method calls!).

You only need to declare that a variable is from an outer scope if you're re-assigning it. In Python 2, you can only do that with global variables (via global var), in Python 3 you can do it for abritarily nested scopes (e.g. closures) using nonlocal var.

Using a nonlocal variable as in you example doesn't require global. It does, however, as soon as you assign the variable (with the aforementioned definition of assignment) anywhere within that scope - so add a line some_var = ... after the line where you're using it and you'll get an UnboundLocalError. Refer to the documentation for the nitty gritty details.

2

You only need to use global if you intend to assign to the variable, for reading the variable this is not necessary. This difference is not arbitrary, though it may seem like it at first glance.

When reading a value the interpreter can just look for a local variable named some_var, if it cannot find it then it looks for a global variable of that name. These are simple and straight forward semantics.

When assigning values to a variable the interpreter needs to know whether you intend to assign to a local variable some_var or a global variable. The interpreter assumes that some_var = 2 when called within a function is assigning to a local variable, this makes sense since this is the most common case. For the relatively rare times when you want to assign to a global variable from within a function then you use the global modifier global some_var = 2.

Tendayi Mawushe
  • 25,562
  • 6
  • 51
  • 57
1

It depends on the usage of the variable in the function Python variable scope error

Community
  • 1
  • 1
Peter Kelly
  • 14,253
  • 6
  • 54
  • 63
1

Assigning a value to a name makes the name local, unless the name is explicitly declared global.

a = 12

def foo():
   a = 42
   print a   # uses local

foo()
>>> 42

def foo():
   global a
   a = 42

foo()
print a
>>> 42

If a name is not assigned to, it is global.

a = 12

def foo():
   print a   # uses global

foo()
>>> 12

In short, you only have to explicitly declare a name global if you will be assigning to it. If you are just reading from it, you can use it at will. However, if you ever assign to the variable, it will be considered local in that function unless you declared it global.

b = 5

def foo():
   print b
   b = 7

foo()
>>> ???

Since b is assigned to in foo() and not declared global, Python decides at compile time that b is a local name. Therefore b is a local name throughout the whole function, including at the print statement before the assignment.

Therefore the print statement gives you an error, because the local name b has not been defined!

kindall
  • 178,883
  • 35
  • 278
  • 309