So far we have built only tiny toy programs. To build bigger ones, we need to be able to name things so as to refer to them later. We also need to write expressions whose result depends upon one or more other things.

So far, if we wished to use a sub-expression twice or more in a single expression, we had to type it multiple times:

```
Python
>>> 200 * 200 * 200
8000000
```

Instead, we can define our own name to stand for the result of evaluating an expression, and then use the name as we please:

```
Python
>>> x = 200
>>> x * x * x
8000000
```

We can update the value associated with the name and try the calculation again:

```
Python
>>> x = 200
>>> x * x * x
8000000
>>> x = 5 + 5
>>> x * x * x
1000
```

Because of this ability to vary the value associated with the name, things like `x`

are called *variables*. We can use any name we like for a variable, so long as it does not clash with any of Python’s built in *keywords*:

`and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield`

In Python, we use lower case letters or words for variable names. For example `x`

, `weight`

, or `total`

. If we wish to use multiple words, we separate them with underscores. For example `first_string`

or `total_of_subtotals`

.

We can make a *function*, whose value depends upon some input. We call this input an *argument* – we will be using the word “input” later in the book to mean something different:

```
Python
>>> def cube(x): return x * x * x
...
>>> cube(10)
1000
>>> answer = cube(20)
>>> answer
8000
```

Note that we had to press the Enter key twice when defining the function: we shall discover why momentarily. What are the parts to this definition of the function `cube`

? We write ** def**, followed by the function name, its argument in parentheses, and a colon. Then, we calculate

`x * x * x`

and use `return`

We need the word ** return** because not all functions return something. For example, this function prints a string given to it twice to the screen, but does not return a value:

```
Python
>>> def print_twice(x):
... print(x)
... print(x)
...
>>> print_twice('Ha')
Ha
Ha
>>> print_twice(1)
1
1
```

Notice this function spans multiple lines. It can operate on both strings and numbers. Now you can see why we needed to press Enter twice when defining the `cube`

and `print_twice`

functions – so that Python knows when we have finished entering a multi-line function.

Each of the `print(x)`

lines in `print_twice`

is *indented* (moved to the right by insertion of four spaces). This helps us to show the structure of the program more clearly, and in fact is a requirement – Python will complain if we do not do it:

```
Python
>>> def print_twice(x):
... print(x)
File "<stdin>", line 2
print(x)
^
IndentationError: expected an indented block
```

You will come across this error frequently as you learn to indent your Python programs correctly.

We can use the keywords ** if** and

`else`

```
Python
>>> def neg(x):
... if x < 0:
... return True
... else:
... return False
```

We can test it like this:

```
Python
>>> neg(1)
False
>>> neg(-1)
True
```

Notice the indentation of each part of this function, after every line which ends with a colon – again, it is required. We can write it using fewer lines, if it will fit:

```
Python
>>> def neg(x):
... if x < 0: return True
... else: return False
```

Of course, our function is equivalent to just writing

```
Python
>>> def neg(x):
... return x < 0
```

because `x < 0`

will evaluate to the appropriate boolean value on its own – `True`

if *x* < 0 and `False`

otherwise. Here is another function, this time to determine if a given string is a vowel or not:

```
Python
>>> def is_vowel(s):
... return s == 'a' or s == 'e' or s == 'i' or s == 'o' or s == 'u'
>>> is_vowel('x')
False
>>> is_vowel('u')
True
```

If we need to test for more than one condition we can use the ** elif** keyword (short for “else if”):

```
Python
>>> def sign(x):
>>> if x < 0: return -1
>>> elif x == 0: return 0
>>> else: return 1
```

This function returns the sign of a number, irrespective of its magnitude. Of course, this could be written without ** elif**. Can you see how?

There can be more than one argument to a function. For example, here is a function which checks if two numbers add up to ten:

```
Python
>>> def add_to_ten(a, b):
... return a + b == 10
>>> add_to_ten(6, 4)
True
>>> add_to_ten(6, 5)
False
```

The result is a boolean. We use the function in the same way as before, but writing two numbers this time, one for each argument the function expects. Finally, let us use the `+`

operator in a different way, to concatenate strings:

```
Python
>>> def welcome(first, last):
... print('Welcome, ' + first + ' ' + last + '! Enjoy your stay.')
>>> welcome('Richard', 'Smith')
Welcome, Richard Smith! Enjoy your stay.
```

A *recursive* function is one which uses itself in its own definition. Consider calculating the factorial of a given number – for example the factorial of 4 (written 4! in mathematics) is 4 × 3 × 2 × 1. Here is a recursive function to calculate the factorial of a positive number.

```
Python
>>> def factorial(a):
... if a == 1:
... return 1
... else:
... return a * factorial(a - 1)
```

For example:

```
Python
>>> factorial(4)
24
>>> factorial(100)
933262154439441526816992388562667004
907159682643816214685929638952175999
932299156089414639761565182862536979
208272237582511852109168640000000000
00000000000000
```

How does the evaluation of `factorial(4)`

proceed?

For the first three steps, the ** else** part of the conditional expression is chosen, because the argument

`a`

is greater than one. When the argument is equal to one, we do not use `factorial`

again, but just evaluate to `1`

. The expression built up of all the multiplications is then evaluated until a value is reached: this is the result of the whole evaluation. It is sometimes possible for a recursive function never to finish – what if we try to evaluate `factorial(-1)`

?The expression keeps expanding, and the recursion keeps going. Helpfully, Python tells us what is going on:

```
Python
>>> factorial(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in factorial
File "<stdin>", line 5, in factorial
File "<stdin>", line 5, in factorial
[Previous line repeated 995 more times]
File "<stdin>", line 2, in factorial
RecursionError: maximum recursion depth exceeded in comparison
```

We do not use recursive functions often in Python, preferring the methods of repeated action described in the next chapter. But it can be interesting to think about how they work, and some of the questions at the end of the chapter invite you to do just that.

Almost every program we write will involve functions such as those shown in this chapter, and many larger ones too – using functions to split up a program into small, easily understandable chunks is the basis of good programming.

Now that we are writing slightly larger programs which might span multiple lines, new types of mistake are available to us. A common mistake is to forget the colon at the end of a line. For example, here we forget it after an ** if**:

```
Python
>>> def neg(x):
... if x < 0
File "<stdin>", line 2
if x < 0
^
SyntaxError: invalid syntax
```

Syntax is a word for the arrangement of symbols and words to make a valid program. If we forget the proper indentation, Python complains too:

```
Python
>>> def neg(x):
... if x < 0:
... return True
File "<stdin>", line 3
return True
^
IndentationError: expected an indented block
```

We must also remember to avoid using one of Python’s keywords as a variable or function name, even in an otherwise valid program:

```
Python
>>> def class(x): return '30 pupils'
File "<stdin>", line 1
def class(x): return '30 pupils'
^
SyntaxError: invalid syntax
```

Another common mistake is to omit the ** return** in a function:

```
Python
>>> def double(x): x * 2
...
>>> double(5)
>>>
```

In this case, Python accepts the function, and we only discover our mistake when we try to use it.

We have learned how to give names to our values so as to use and reuse them in different contexts, and to update the values associated with such names. We have written functions whose result depends upon one or more arguments, including multi-line functions. We have seen how to choose a course of action based upon testing the value associated with a name.

Finally, we have experimented with recursive functions to perform repeated processing of one or more arguments. We have explained, though, that recursion is not ordinarily used in Python. In the next chapter, we will introduce the standard Python mechanisms for repeated actions or calculations.

*Questions 5–8 are optional – we do not often use recursive functions in Python.*

Write a function which multiplies a given number by ten.

Write a function which returns

`True`

if both of its arguments are non-zero, and`False`

otherwise.Write a function

`volume`

which, given the width, height, and depth of a box, calculates its volume. Write another function`volume_ten_deep`

which fixes the depth at 10. It should be implemented by using your`volume`

function.Write a function

`is_consonant`

which, given a lower-case letter in the range`’a’`

…`’z’`

, determines if it is a consonant.Can you suggest a way of preventing the non-termination of the

`factorial`

function in the case of a zero or negative argument?Write a recursive function

`sum_nums`

which, given a number*n*, calculates the sum 1 + 2 + 3 + … +*n*.Write a recursive function

`power(x, n)`

which raises`x`

to the power`n`

.Write a recursive function to list the factors of a number. For example,

`factors(12)`

should print:`1 2 3 4 6 12`