# 9.4 Aliasing and Scope

## Aliasing

If `a` refers to an object and you assign `b = a`, then both variables refer to the same object:

```python
>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
```

The association of a variable with an object is called a reference. In this example, there are two references to the same object.

An object with more than one reference has more than one name, so we say that the object is aliased.

If the aliased object is mutable, changes made with one alias affect the other:

```python
>>> b[0] = 17
>>> print(a)
[17, 2, 3]
```

Although this behavior can be useful, it is error-prone. In general, it is safer to avoid aliasing when you are working with mutable objects.

## Variables and parameters are local

When you create a variable inside a function, it is local, which means that it only exists inside the function. For example:

```python
def cat_twice(part1, part2):
    cat = part1 + part2
    print_twice(cat)
```

This function takes two arguments, concatenates them, and prints the result twice. Here is an example that uses it:

```python
>>> line1 = 'Bing tiddle '
>>> line2 = 'tiddle bang.'
>>> cat_twice(line1, line2)
Bing tiddle tiddle bang.
Bing tiddle tiddle bang.
```

When `cat_twice` terminates, the variable `cat` is destroyed. If we try to print it, we get an exception:

```python
>>> print(cat)
NameError: name 'cat' is not defined
```

Parameters are also local. For example, outside `print_twice`, there is no such thing as `part1` or `part2`.

## Global variables

Variables created outside the function belong to the special frame called `__main__`. Variables in `__main__` are sometimes called global because they can be accessed from any function. Unlike local variables, which disappear when their function ends, global variables persist from one function call to the next.

It is common to use global variables for constants; that is, variables that do not change. For example, some programs use constants to indicate the minimum or maximum number of a dataset like the max level of a game could be set to 10.

If you try to reassign a global variable, you might be surprised. The following example is supposed to keep track of whether the function has been called:

```python
been_called = False

def example2():
    been_called = True         # WRONG
```

But if you run it you will see that the value of `been_called` doesn’t change. The problem is that the`example2` function creates a new local variable named `been_called`. The local variable goes away when the function ends, and has no effect on the global variable.

To reassign a global variable inside a function you have to declare the global variable before you use it:

```python
been_called = False

def example2():
    global been_called
    been_called = True
```

The global statement tells the interpreter something like, “In this function, when I say `been_called`, I mean the global variable; don’t create a local one.”

Here’s an example that tries to update a global variable:

```python
count = 0

def example3():
    count = count + 1          # WRONG
```

If you run it you get the following error message:

```python
UnboundLocalError: local variable 'count' referenced before assignment
```

Python assumes that `count` is local, and under that assumption you are reading it before writing it. The solution, again, is to declare count global.

```python
def example3():
    global count
    count += 1
```

If a global variable refers to a mutable value, you can modify the value without declaring the variable global:

```python
known = [10, 20]

def example4():
    known[1] = 30
```

If a program has a lot of global variables and modifies them frequently, they can make programs hard to debug.

## Stack diagrams

To keep track of which variables can be used where, it is sometimes useful to draw a stack diagram. Like state diagrams, stack diagrams show the value of each variable, but they also show the function to which each variable belongs.

Each function is represented by a frame. A frame is a box with the name of a function beside it and the parameters and variables of the function inside it. The stack diagram for the previous example looks like this:

| [![](http://www.cs.swarthmore.edu/courses/cs21book/build/_images/stack.png)](http://www.cs.swarthmore.edu/courses/cs21book/build/_images/stack.png)Stack diagram |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| *Stack diagram*                                                                                                                                                  |

The order of the stack shows the flow of execution. `print_twice` was called by `cat_twice`, and `cat_twice` was called by `__main__`, which is a special name for the topmost function. When you create a variable outside of any function, it belongs to `__main__`.

Each parameter refers to the same value as its corresponding argument. So, part1 has the same value as `chant1`, `part2` has the same value as `chant2`, and `param` has the same value as cat.

If an error occurs during a function call, Python prints the name of the function, and the name of the function that called it, and the name of the function that called that, all the way back to the top most function.

To see how this works, create a Python script named `tryme2.py` that looks like this:

```python
def print_twice(param):
    print(param)
    print(param)
    print(cat)

def cat_twice(part1, part2):
    cat = part1 + part2
    print_twice(cat)

chant1 = "Pie Jesu domine, "
chant2 = "Dona eis requim."
cat_twice(chant1, chant2)
```

We’ve added the statement, `print(cat)` inside the `print_twice` function, but `cat` is not defined there. Running this script will produce an error message like this:

```python
Traceback (innermost last):
  File "tryme2.py", line 12, in <module>
    cat_twice(chant1, chant2)
  File "tryme2.py", line 8, in cat_twice
    print_twice(cat)
  File "tryme2.py", line 4, in print_twice
    print(cat)
NameError: global name 'cat' is not defined
```

This list of functions is called a traceback. It tells you what program file the error occurred in, and what line, and what functions were executing at the time. It also shows the line of code that caused the error.

Notice the similarity between the traceback and the stack diagram. It’s not a coincidence. In fact, another common name for a traceback is a stack trace.

## List arguments

When you pass a list to a function, the function gets a reference to the list. If the function modifies a list parameter, the caller sees the change. For example, `delete_head` removes the first element from a list:

```python
def delete_head(t):
    del t[0]
```

Here’s how it is used:

```python
>>> letters = ['a', 'b', 'c']
>>> delete_head(letters)
>>> print(letters)
['b', 'c']
```

The parameter t and the variable letters are aliases for the same object. The stack diagram looks like the following:

| [![](http://www.greenteapress.com/thinkpython/html/thinkpython017.png)](http://www.greenteapress.com/thinkpython/html/thinkpython017.png)Stack Diagram |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| *Stack Diagram*                                                                                                                                        |

Since the list is shared by two frames, I drew it between them.

It is important to distinguish between operations that modify lists and operations that create new lists. For example, the append method modifies a list, but the + operator creates a new list:

```python
>>> t1 = [1, 2]
>>> t2 = t1.append(3)
>>> print(t1)
[1, 2, 3]
>>> print(t2)
None

>>> t3 = t1 + [4]
>>> print(t3)
[1, 2, 3, 4]
```

This difference is important when you write functions that are supposed to modify lists. For example, this function does not delete the head of a list:

```python
def bad_delete_head(t):
    t = t[1:]              # WRONG!
```

The slice operator creates a new list and the assignment makes t refer to it, but none of that has any effect on the list that was passed as an argument.

An alternative is to write a function that creates and returns a new list. For example, tail returns all but the first element of a list:

```python
def tail(t):
    return t[1:]
```

This function leaves the original list unmodified. Here’s how it is used:

```python
>>> letters = ['a', 'b', 'c']
>>> rest = tail(letters)
>>> print(rest)
['b', 'c']
```
