We've been making liberal use of functions,
like

, `int`

, `len`

, and `print`

.
Often, though, it's useful to define `range`*new* functions that aren't
built into the language. Why?

We may have the same computation multiple places in the program. It may be tempting to “copy and paste” the code performing the computation from one place in the program to another, but capable programmers know always to avoid this: Inevitably, the code has to be modified later on, but we will forget to modify both places, so the program will be erroneous.

We may just have a very large program that is getting difficult to think about, and so we might use functions to break the computation up into “brain-sized” chunks that one can develop independently.

Defining a new function is relatively straightforward. Here is an
example, which defines a new function named

for
squaring a value:`sq`

**def** `sq`(`x`):

**return** `x` ** `2`

The

keyword tells Python that we are starting to
define a function. This is followed by the name we're giving to
our new function, then a set of parentheses listing the names of
the arguments that will be passed into the functions.
These names are called **def****parameters**. This is followed by a
colon, and all indented lines below that form the function's
**body**.

Notice a mild distinction between the words
*argument* and *parameter*.
In

, the name `sq`

is a `x`*parameter*.
When we call

, as in the line
“`sq`

”,
the value we include in the parentheses — 5, here —
is the `print`(`3.14` * `sq`(`5`))*argument*.
The parameter variable (

here) will be set to whatever
argument is passed into the function; in this example, a call to
“`x`

” will lead to
setting `sq`(`5`)

to 5
before executing the function's body.`x`

The function's body says what to do when the function is
called. It is a list of one or more statements.
In this case, there is just one statement, a

statement that says that we are done with the function, and the
function's value is whatever value appears after **return**

.
In the case of **return**

, we return 5², which is 25.
Since `sq`(`5`)

is 25 and 3.14 ⋅ 25 is 78.5, the
line “`sq`(`5`)

” displays 78.5.`print`(`3.14` * `sq`(`5`))

Naturally, functions can call other functions.

**def** `quad`(`y`):

`y2` = `sq`(`y`)

**return** `sq`(`y2`)

For the call

,
we'll execute `quad`(`3`)

's body with `quad`

being 3.
The first line of `y`

says to compute `quad`

,
which translates to calling `sq`(`y`)

with an argument of 3.
The `sq`

function returns 9 in this case, so `sq`

sets `quad`

to be the value 9.
The second line of `y2`

says to compute `quad`

,
so we enter `sq`(`y2`)

this time with an argument of 9.
That returns 81, and `sq`

says to return this.
So `quad`

turns out to be 81.`quad`(`3`)

The body can naturally be more complex.

**def** `find_multiple`(`multiplier`, `nums`):

**for** `n` **in** `nums`:

**if** `n` % `multiplier` == `0`:

**return** `n`

In this case,

expects two arguments:
The first (named `find_multiple`

) should be an integer,
while the second (named `multiplier`

) should be a list of
integers.`nums`

For example, suppose that a program were to call
“

”.`find_multiple`(`5`, [`2`, `3`, `5`, `8`, `13`, `21`, `34`, `55`, `89`])

Before starting the function call, the computer first configures the

parameter variable as 5 and the`multiplier`

parameter variable as`nums``[`

.`2`,`3`,`5`,`8`,`13`,`21`,`34`,`55`,`89`]We begin the function's body, in this case entering the loop with

being the first value in`n`

, which is 2. Since 2 % 5 is 2, we do not enter the`nums`

statement.**if**We go for another iteration, with

being the second value in`n`

, which is 3. Since 3 % 5 is 3, we do not enter the`nums`

statement.**if**We go for another iteration, with

being the third value in`n`

, which is 5. Since 5 % 5 is 0, we enter the`nums`

statement.**if**We have now reached a

statement. This says to stop the function immediately; we do not go for any more iterations of the**return**

loop. The function's value is the value following the word**for**

; in this case we find**return**

, whose value is 5, so the value of “`n`

” turns out to be 5.`find_multiple`(`5`, [`2`,`3`,`5`,`8`,`13`,`21`,`34`,`55`,`89`])

Notice that since the function stopped as soon as it reached
a

statement, it never discovered that 55 is also
a multiple of 5.**return**

And what if we gave

a list where it
never reaches a `find_multiple`

statement, as in
“**return**

”?
Once Python “goes off the end” of a function's body,
it simply returns with a special value of `find_multiple`(`3`, [`1`, `2`, `4`, `8`, `16`])

.
As it happens, that seems a reasonable value to return for this
problem.**None**

However, what if we wanted

to have a
value of 0 if a multiple isn't found in the list? That would be
easy enough to do: We'd just add an additional `find_multiple`

statement outside the loop.**return**

**def** `find_multiple`(`multiplier`, `nums`):

**for** `n` **in** `nums`:

**if** `n` % `multiplier` == `0`:

**return** `n`

**return** `0`

This function has two

statements. But in
executing the program, whichever one is reached first will be
the only one executed. After all, **return**

says to stop
the function's computation immediately.
In this case, once a multiple is found, then the first **return**

statement returns that multiple; but if there is no multiple,
then it reaches the final **return**

statement and returns 0.**return**