Creating functions in Python (Part 3)

Types of address books

We've been talking about different types of address books. Let's use more precise terms.

  • Names in a temporary address book are local.
  • Names in the permanent address book are global.

A name defined inside a function call has meaning only while that function call is taking place. In this sense, it is local to that function call. In contrast, a name that has meaning outside function calls is global.

Order of name search

  • Not inside a user-defined function call: global
  • Inside a function call: local, global

When the computer encounters a name and is not in the middle of a call to a user-defined function, what does it do? It looks in the permanent address book. If it is in the middle of a call to a user-defined function, then a temporary address book has been created. It first looks for the name in the temporary address book, and if the name cannot be found there, only then does it look in the permanent address book.

Optional information

Python has statements global and nonlocal to change search order.

You can change the search order by giving information about where a name is to be found.

Optional information

More complicated: functions defined inside functions.

In fact it is a bit more complicated when you define functions inside of other functions. We won't be concerned with such functions or with changing search order.


Reusing names

Glimpse of the future

Reusing names can improve clarity.

Good habit

When it affects clarity, reusing names should be avoided.

When it comes to using names more than once in a program, the choice depends on whether the result is correct and whether it is clear. Since in some cases reuse is the better choice, let's look a little more carefully at how it works.

Global: variable, constant, function

Names in the permanent address book refer to variables, constants, and functions. Since constants behave like variables, we'll just look at variables and functions.

Local: variable, constant, function, parameter

Since it is possible to define a function inside a function, possibilities for entries in temporary address books include variables, constants, functions, and parameters, though for this course we'll avoid functions within functions and, like for global names, consider constants and variables together.

Types of reuse:

  • Global and global
  • Local and local (same address book)
  • Local and local (different address books)
  • Global and local

We'll first look at ways names can be reused inside the same address book and then what happens when a name appears in two different address books.


Global and global reuse

The variable x is assigned the value 5.
The variable x, which was assigned the value 5, is assinged the value 6. The old value 5 is lost when the address book is updated with the address of the new variable.

Observation

The new use of a global name replaces the old use.

We already know what happens when a name is used to store a value and then another value: the old value is lost when the address book is updated with the address of the new variable.

As we'll see in the upcoming examples, the same thing happens when either or both of the global names is the name of a function.


More global and global reuse

In this example, ape has two global names, first as a variable and then as a function:

ape = 10

def ape(num):
    return num

Will we get the value 10 when we print ape? No. The following code

ape = 10

def ape(num):
    return num

print(ape)

gives the output <function ape at 0x7ffb565f6a60>. But that's not a surprise. Although first the permanent address book listed 10 as the value for x, as soon as the function was defined, the name was no longer assigned to 10 but instead to the function. We can use it as a function, with the result of the function call on 2, ape(2), returning the value 2. This can be observed when we run the code

ape = 10

def ape(num):
    return num

print(ape)
print(ape(2))

which gives the output

<function ape at 0x7f72f8079ae8>
2

What if instead we used the same name for a function and then a variable? In the following code, bear appears first in a function definition and then as the name of a variable. Let's see what happens when we try to print bear. The following code

def bear(num):
    return num

bear = 10

print(bear)

gives the output 10. This time we obtain the value 10, which makes sense, since in the permanent address book, first bear was given the name of a function, and then afterwards it was changed to store the value 10. What will happen if we try to use bear as a function? The following code

def bear(num):
    return num

bear = 10

print(bear)
print(bear(2))

gives the output

10
Traceback (most recent call last):
  File "<string>", line 7, in <module>
TypeError: 'int' object is not callable

The error message tells us that an int object is not callable, that is, that bear is a variable, not a function. This isn't a surprise. The function definition was lost when bear was assigned to an integer value.

In this example, we'll start by using cat as the name of a function. We can use the function on the input 10, cat(10), and obtain the result 10. This is observed when the following code is run

def cat(num):
    return num

print(cat(10))

which gives the output 10.

If we then use cat as the name of a different function, when we use the name in the function call it is the second function, not the first function, that is being used. For example, the following code

def cat(num):
    return num

print(cat(10))

def cat(num):
    return num + 1

print(cat(10))

gives the output

10
11

Again, this isn't a surprise: the first function was lost as soon as the second function was defined. We expect the same behaviour in any address book, permanent or temporary. For example, consider the following code

def keep(dog):
    dog = 4
    return dog
    
print(keep(1))

which gives the output 4. In this example, dog is used as a parameter and then as the name of a variable. The value of the parameter is lost when dog is assigned the value 4. So, running keep(1) results in 4 being returned, not 1.

Let's consider another example. The following code

def add_on(num):
    eel = 10
    eel = 3
    return num + eel
    
print(add_on(1))

gives the output 4. Here eel is first assigned the value 10 and then a new value 3 is assigned. Again, the old value is lost, so calling the function add(1), results in 1 being added to 3, not to 10.


Local and local reuse (same address book)

Observation

The new use of a global name replaces the old use.

Observation

The new use of a local name replaces the old use in the same address book.

To summarize, we have a new observation that matches our old one. That is, whether it is global, in the permanent address book, or local, in a temporary address book, the new use of a name replaces the old use.


Local and local reuse (different address books)

What happens if names are used as local variables in different functions? Our reasoning tells us that using the same name locally in different functions means that the name is used in local address book for function calls, with no interference. Let's see if that is true. Running the code

def one(fox):
    return fox + 1

def two(fox):
    return fox + 2

print(one(10))
print(two(10))

gives the output

11
12

When fox is used as a parameter in function one and in function two, calling one(10) should use the parameter in the temporary address book for one, yielding the expected result of 11, and calling two(10) should use the parameter in the temporary address book for two, yielding the expected result of 12.

Running the code

def one(first):
    goat = first + 1
    return goat

def two(second):
    goat = second + 2
    return goat

print(one(10))
print(two(10))

gives the output

11
12

We obtain similar behaviour when goat appears as a local variable in function one and as a local variable in function two, where in each case only one of the temporary address books is used.

Let's consider another example. Running the code

def one(hamster):
    return hamster

def two(num):
    hamster = num + 3
    return hamster

print(one(10))
print(two(10))

gives the output

10
13

Not surprisingly, there is no problem using hamster as a parameter in function one and as a local variable in function two, again with only one address book being used at a time.

Observation

The same name can be used for local names in two different functions.