32

I'm trying to add or subtract from a defined variable, but how can I overwrite the old value with the new one?

a = 15

def test():
    a = a + 10
    print(a)

test()

Error message:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    test()
  File "test.py", line 4, in test
    a = a +10
UnboundLocalError: local variable 'a' referenced before assignment
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user7351337
  • 329
  • 1
  • 3
  • 3
  • It might be that you need a space after the plus sign. Swift will also give errors if the signs are not separated by a space. – Hedylove Dec 28 '16 at 20:43
  • Please add the code from the image as *text* into the question itself. – Antti Haapala -- Слава Україні Dec 28 '16 at 20:52
  • 2
    This question includes the desired behaviour, a specific problem or error and, unless we're *really* splitting hairs over a single line, the shortest code necessary to reproduce it. It's undeniably a dupe of the question referenced by @approxiblue, but probably a better one, since that question uses augmented assignment (for which the error message makes no sense at all to a beginner), and the accepted answer irresponsibly advises only the use of `global` or `nonlocal` rather than avoidance of globals in the first place. – Zero Piraeus Dec 29 '16 at 02:43
  • @ZeroPiraeus Do you want to reopen then reclose this as a duplicate? – approxiblue Dec 29 '16 at 02:46
  • @ZeroPiraeus Open now, work your gold badge magic. – approxiblue Dec 29 '16 at 06:39
  • @approxiblue left to my own devices, I would probably have let some random passerby decide whether to change the dupe target (wouldn't want to be thought to be taking advantage), but since you insist ... – Zero Piraeus Dec 29 '16 at 06:44

8 Answers8

38

The error that you get when you try to run your code is:

UnboundLocalError: local variable 'a' referenced before assignment

… which, on the face of it, seems strange: after all, the first statement in the code above (a = 15) is an assignment. So, what's going on?

Actually, there are two distinct things happening, and neither of them are obvious unless you already know about them.

First of all, you actually have two different variables:

  • The a in your first line is a global variable (so called because it exists in the global scope, outside of any function definitions).

  • The a in the other lines is a local variable, meaning that it only exists inside your test() function.

These two variables are completely unrelated to each other, even though they have the same name.

A variable is local to a function if there's a statement assigning to it inside that function - for instance, your a = a +10 line.

Even so, the error still looks strange - after all, the very first thing you do inside test() is assign to a, so how can it be referenced beforehand?

The answer is that, in an assignment statement, Python evaluates everything on the right hand side of the = sign before assigning it to the name on the left hand side – so even though the assignment is written first in your code, a gets referenced first in that right hand side: a +10.

There are two ways you can get around this. The first is to tell Python that you really want the a inside test() to be the same a in the global scope:

def test():
    global a
    a = a + 10
    print(a)

This will work, but it's a pretty bad way to write programs. Altering global variables inside functions gets hard to manage really quickly, because you usually have lots of functions, and none of them can ever be sure that another one isn't messing with the global variable in some way they aren't expecting.

A better way is to pass variables as arguments to functions, like this:

a = 15

def test(x):
    x = x + 10
    print(x)

test(a)

Notice that the name doesn't have to be the same - your new definition of test() just says that it accepts a value, and then does something with it. You can pass in anything you like – it could be a, or the number 7, or something else. In fact, your code will always be easier to understand if you try to avoid having variables with the same name in different scopes.

If you play with the code above, you'll notice something interesting:

>>> a = 15
>>> test(a)
25
>>> a
15

a didn't change! That's because although you passed it into test() and it got assigned to x, it was then x that got changed, leaving the original a alone.

If you want to actually change a, you need to return your modified x from the function, and then reassign it back to a on the outside:

>>> a = 15
>>> 
>>> def test(x):
...     x = x + 10
...     print(x)
...     return x
... 
>>> a = test(a)
25
>>> a
25
Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
  • 2
    Before someone points out that `x` actually got *replaced* rather than *changed* … I know. I already had to cover a fair bit of ground to properly answer the question, though, and decided not to open that particular can of worms. – Zero Piraeus Dec 28 '16 at 21:37
  • This was a great answer, thanks. So, to be sure, there's really no way to define the test function such that it will actually change "a"? I.e., there's no way to incorporate the external reassignment step a = test(a) into the function itself through some more complicated code? – user26866 Dec 23 '20 at 19:21
4

You're modifying a variable a created in the scope of the function test(). If you want the outer a to be modified, you could do:

a = 15

def test():
    global a
    a = a + 1
    print(a)

test()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
d4vsanchez
  • 1,904
  • 1
  • 14
  • 24
  • 2
    While using `global` solves the immediate problem, it's bad programming practice, and you shouldn't really be teaching it as a solution to a beginner without also pointing out that it's both poor practice and easily avoided. `global` really shouldn't be used by someone who doesn't already thoroughly understand why it's usually the wrong thing to do. – Zero Piraeus Dec 28 '16 at 21:33
  • You are right and I present my excuses for not expressing the bad choice of using global. I will update my answer in a moment to express that. Thanks so much. @ZeroPiraeus – d4vsanchez Dec 28 '16 at 21:45
3

I would do it this way:

def test(a):
    a = a +10
    return a

print(test(15))

Note that in the version hereby proposed there are some things differing from yours.

First, what I wrote down would create a function that has, as an input, the value a (in this case set to 15 when we call the function -already defined in the last line-), then assigns to the object a the value a (which was 15) plus 10, then returns a (which has been modified and now is 25) and, finally, prints a out thanks to the last line of code:

print(test(15))

Note that what you did wasn't actually a pure function, so to speak. Usually we want functions to get an input value (or several) and return an input value (or several). In your case you had an input value that was actually empty and no output value (as you didn't use return). Besides, you tried to write this input a outside the function (which, when you called it by saying test(a) the value a was not loaded an gave you the error -i.e. in the eyes of the computer it was "empty").

Furthermore, I would encourage you to get used to writing return within the function and then using a print when you call it (just like I wrote in the last coding line: print(test(15))) instead of using it inside the function. It is better to use print only when you call the function and want to see what the function is actually doing.

At least, this is the way they showed me in basic programming lessons. This can be justified as follows: if you are using the return within the function, the function will give you a value that can be later used in other functions (i.e. the function returns something you can work with). Otherwise, you would only get a number displayed on screen with a print, but the computer could not further work with it.

P.S. You could do the same doing this:

def test(a):
    a +=10      
    return a

print(test(15))
blackcub3s
  • 189
  • 1
  • 4
1
a = 15

def test():
    global a
    a = a + 10
    print(a)

test()

If you want to change the values of local variables inside a function you need to use the global keyword.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
NAIDU
  • 169
  • 7
0

The scope of the variable is local to the block unless defined explicitly using keyword global. There is another way to access the global variable local to a function using the globals function:

a = 15

def test():
    a = globals()['a']
    a += 10
    print(a)

test()

The above example will print 25 while keeping the global value intact, i.e., 15.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MaNKuR
  • 2,578
  • 1
  • 19
  • 31
0

The issue here is scope.

The error basically means you're using the variable a, but it doesn't exist yet. Why? The variable a has not been declared in the test() function. You need to "invite" global variables before using them.

There are several ways to include a variable in the scope of a function. One way is to use it as a parameter like this:

def test(number):
    number = number + 10
    print(number)

Then implement it as:

test(a)

Alternatively, you could also use the global keyword to show the function that a is a global variable. How? Like this:

def test():
    global a
    a = a + 10
    print(a)

Using the global keyword just tells the test where to look for a.

With the first method, you can't modify the global variable a, because you made a copy of it and you're using the copy. In the second method, though, any changes you make in the function test() to a, will affect the global variable a.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
TeeBeeGee
  • 1
  • 1
-3
# All the mumbo jumbo aside, here is an experiment 
# to illustrate why this is something useful 
# in the language of Python:
a = 15    # This could be read-only, should check docs

def test_1():
    b = a + 10    # It is perfectly ok to use 'a'
    print(b)

def test_2():
    a = a + 10    # Refrain from attempting to change 'a'
    print(a)

test_1()    # No error
test_2()    # UnboundLocalError: ...
Down the Stream
  • 639
  • 7
  • 10
-4

Your error has nothing to do with it being already defined…

A variable is only valid inside its so-called scope: If you create a variable in a function it is only defined in this function.

def test():
   x = 17
   print(x) # Returns 17

test()
print(x) # Results in an error.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nudin
  • 351
  • 2
  • 11