2

I've started teaching myself python, and have noticed something strange to do with global variables and scope. When I run this:

x = 2 
y = 3 
z=17 
def add_nums():
    y = 6 
    return z+y

The result of 23 is printed... However, when I expand the return to be:

x = 2 
y = 3 
z=17 
def add_nums(): 
    y = 6 
    z = z + y 
    return z

I get the following error on line 6:

Local name referenced but not bound to a value.
A local name was used before it was created. You need to define the     
method or variable before you try to use it.

I'm confused as why I am getting an error here, as z is global an accessible.

Vaderico
  • 629
  • 2
  • 8
  • 24
  • ["Explicit is better then implicit"](https://www.python.org/dev/peps/pep-0020/) use the `global` keyword after the function name, python assumes `z` to be local in that case – mjb4 May 25 '15 at 13:43
  • I think the same question has been answered properly here http://stackoverflow.com/questions/423379/using-global-variables-in-a-function-other-than-the-one-that-created-them already – Ales Maticic May 25 '15 at 13:52

4 Answers4

7

When a variable is on the left side of an equal sign python will create a local variable. When the variable is on the right side of the equal sign python will try to look for a local variable and if he can't find one then it will use a global variable. In your example, z is on the right and left side of the equal sign, to avoid ambiguities python raises an error. You need to use the global syntax to avoid this:

x = 2 
y = 3 
z = 17 
def add_nums(): 
    global z
    y = 6 
    z = z + y 
    return z
João Abrantes
  • 4,772
  • 4
  • 35
  • 71
6

Python determines scope by binding operations. Assignment is a binding operation, as is importing, using a name as a target in except .. as, with .. as or a for loop, or by creating a function or a class.

When a name is bound in a scope, it is local to that scope. If a name is used but not bound, it is non-local; the compiler will determine at compile time what the scope should be then. In your case there is no parent scope other than the global scope, so any name not bound to is considered a global.

Since your second example binds to z (you used z =, an assignment), the name is local to the function.

If a name is bound to in a scope but you want to tell Python that it should be global anyway, you need to do so explicitly:

x = 2 
y = 3 
z=17 
def add_nums(): 
    global z
    y = 6 
    z = z + y 
    return z

The global z line tells the compiler that z should be a global, even though you bind to it.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Hello @Martijn Pieters. Is it possible to bind a variable locally after you've declared it globally in a function ? Normally I'd just use another variable, I'm just asking for education purposes only (pushing what's possible and what not). For instance, in the example you provided above, after doing `global z` and you alter `z`, that global `z` now has the value of `23`. What if I wanted to, before the return `z` line, rebind it locally under the same name `z` (sort of make a copy of it under the same name) and do a bunch of stuff with it and return the copy value. AFAIK this is not possible. – Marius Mucenicu Jun 10 '19 at 05:52
  • 1
    @George that’s not possible. A variable’s scope is set at compile time, and can’t be dynamically changed at runtime, not while running the very code object that the uses the variable. – Martijn Pieters Jun 10 '19 at 08:30
  • 1
    @George if all you want to do is have a local copy of the global value to return at the end of the function, use a local variable with a different name. `z_local = z` if `z references an immutable type is enough. – Martijn Pieters Jun 10 '19 at 08:33
  • Thank you for your answer @Martijn Pieters! I thought that only the function header is evaluated at compile time and the body at runtime, my information seems to be wrong. So the function body does get analysed at compile time in terms of what is what, but not evaluated as in reducing the value of expressions to their atomic value and so on. – Marius Mucenicu Jun 10 '19 at 18:39
3

Let's analyse the marked line.

x = 2
y = 3 
z = 17 
def add_nums(): 
    y = 6 
    z = z + y  <--- THIS LINE
    return z

z ... a new local variable is created
= ... we are going to assign a value to it
z ... this variable exists (the new local one) but it has no value yet.
+ y ... this part is not reached.

The result is an error message "UnboundLocalError: local variable 'z' referenced before assignment".

dlask
  • 8,776
  • 1
  • 26
  • 30
1

If you want the names y and z inside the function body to reference your global variables when you assign something to them, you must declare them like this:

x = 2 
y = 3 
z=17 
def add_nums(): 
    global y 
    global z 
    y = 6
    z = z + y
    return z

Otherwise, when you perform an assignment to the variables y and z inside the function, you create different names that exist only in the local scope of the function.

As pointed out in the comments, you can reference a global variable and even modify it if it is mutable (i.e. append something to a list) without explicitly declaring it global as long as you don't try to assign to it.

Eric Appelt
  • 2,843
  • 15
  • 20
  • 1
    That's not entirely accurate since you can reference y in this way without trouble. – SuperBiasedMan May 25 '15 at 13:56
  • If you just write `y = 6` in the function you are not referencing the global `y` at all, just declaring a local name `y` and binding 6 to it. You can see the difference if you print `y` after running the function. – Eric Appelt May 25 '15 at 14:00
  • Sorry, got mixed up with my comment. His first example doesn't need to reference z and will still work. You only need to declare globals explicitly if Python might think you're declaring a new local. – SuperBiasedMan May 25 '15 at 14:15
  • 1
    I see your point - you are right that the original post is not entirely accurate and I tried to edit it to fix this. – Eric Appelt May 25 '15 at 14:45