*This notebook contains an excerpt from the Python Programming and Numerical Methods - A Guide for Engineers and Scientists, the content is also available at Berkeley Python Numerical Methods.*

*The copyright of the book belongs to Elsevier. We also have this interactive book online for a better learning experience. The code is released under the MIT license. If you find this content useful, please consider supporting the work on Elsevier or Amazon!*

< 3.1 Function Basics | Contents | 3.3 Nested Functions >

# Local Variables and Global Variables¶

Chapter 2 introduced the idea of the memory associated with the notebook where variables created in the notebook are stored. A function also has its own memory block that is reserved for variables created within that function. This block of memory is not shared with the whole notebook memory block. Therefore, a variable with a given name can be assigned within a fucntion without changing a variable with the same name outside of the function. The memory block associated with the function is opened every time a function is used.

**TRY IT!** What will the value of *out* be after the following lines of code are executed? Note that it is not 6, which is the value *out* is assigned inside of *my_adder*.

```
def my_adder(a, b, c):
out = a + b + c
print(f'The value out within the function is {out}')
return out
out = 1
d = my_adder(1, 2, 3)
print(f'The value out outside the function is {out}')
```

```
The value out within the function is 6
The value out outside the function is 1
```

In *my_adder*, the variable *out* is a **local variable**. That is, it is only defined in the function of *my_adder*. Therefore, it cannot affect variables outside of the function, and actions taken in the notebook outside the function cannot affect it, even if they have the same name. So in the previous example, there is a variable, *out*, defined in the notebook cell. When *my_adder* is called on the next line, Python opens a new memory block for that function’s variables. One of the variables created within the function is another variable, *out*. However, since they are in different memory blocks, the assignment to *out* inside *my_adder* does not change the value assigned to *out* outside the function.

Why have separate function memory blocks rather than a single memory block? Although it may seem like a lot of trouble for Python to separate memory blocks, it is very efficient for large projects consisting of many functions working together. If one programmer is responsible for making one function, and another for making a different function, we would not want each programmer to have to worry about what variable names the other is using. We want them to be able to work independently and be confident that their own work did not interfere with the other’s and vice versa. Therefore, separate memory blocks protect a function from outside influence. The only things from outside the function’s memory block that can affect what happens inside a function are the input arguments, and the only things that can escape to the outside world from a function’s memory block when the function terminates are the output arguments.

The next examples are designed to be exercises in the concept of local variables. They are intentionally very confusing, but if you can untangle them, then you probably understand the local variable within a function. Focus on exactly what Python is doing, in the order that Python does it.

**EXAMPLE:** Consider the following function:

```
def my_test(a, b):
x = a + b
y = x * b
z = a + b
m = 2
print(f'Within function: x={x}, y={y}, z={z}')
return x, y
```

**TRY IT!** What will the value of a, b, x, y, and z be after the following code is run?

```
a = 2
b = 3
z = 1
y, x = my_test(b, a)
print(f'Outside function: x={x}, y={y}, z={z}')
```

```
Within function: x=5, y=10, z=5
Outside function: x=10, y=5, z=1
```

**TRY IT!** What will the value of a, b, x, y, and z be after the following code is run?

```
x = 5
y = 3
b, a = my_test(x, y)
print(f'Outside function: x={x}, y={y}, z={z}')
```

```
Within function: x=8, y=24, z=8
Outside function: x=5, y=3, z=1
```

**TRY IT!** What will the value of m if you print *m* outside of the function?

```
m
```

```
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-5-9a40b379906c> in <module>
----> 1 m
NameError: name 'm' is not defined
```

We can see the value *m* is not defined outside of the function, since it is defined within the function. The opposite is similar, for example, if you define a variable outside a function, but you want to use it inside the function and change the value, you will get the same error.

**EXAMPLE:** Try to use and change the value *n* within the function.

```
n = 42
def func():
print(f'Within function: n is {n}')
n = 3
print(f'Within function: change n to {n}')
func()
print(f'Outside function: Value of n is {n}')
```

```
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-6-85f3215553ae> in <module>
6 print(f'Within function: change n to {n}')
7
----> 8 func()
9 print(f'Outside function: Value of n is {n}')
<ipython-input-6-85f3215553ae> in func()
2
3 def func():
----> 4 print(f'Within function: n is {n}')
5 n = 3
6 print(f'Within function: change n to {n}')
UnboundLocalError: local variable 'n' referenced before assignment
```

The solution is to use the keyword **global** to let Python know this variable is a global variable that it can be used both outside and inside the function.

**EXAMPLE:** Define n as the global variable, and then use and change the value n within the function.

```
n = 42
def func():
global n
print(f'Within function: n is {n}')
n = 3
print(f'Within function: change n to {n}')
func()
print(f'Outside function: Value of n is {n}')
```

```
Within function: n is 42
Within function: change n to 3
Outside function: Value of n is 3
```