We've been making liberal use of functions,
like int
, len
, print
, and range
.
Often, though, it's useful to define 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 sq
for
squaring a value:
def sq(x):
return x ** 2
The def
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 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 sq
, the name x
is a parameter.
When we call sq
, as in the line
“print(3.14 * sq(5))
”,
the value we include in the parentheses — 5, here —
is the argument.
The parameter variable (x
here) will be set to whatever
argument is passed into the function; in this example, a call to
“sq(5)
” will lead to
setting x
to 5
before executing the function's body.
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 return
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 sq(5)
, we return 5², which is 25.
Since sq(5)
is 25 and 3.14 ⋅ 25 is 78.5, the
line “print(3.14 * sq(5))
” displays 78.5.
Naturally, functions can call other functions.
def quad(y):
y2 = sq(y)
return sq(y2)
For the call quad(3)
,
we'll execute quad
's body with y
being 3.
The first line of quad
says to compute sq(y)
,
which translates to calling sq
with an argument of 3.
The sq
function returns 9 in this case, so quad
sets y2
to be the value 9.
The second line of quad
says to compute sq(y2)
,
so we enter sq
this time with an argument of 9.
That returns 81, and quad
says to return this.
So quad(3)
turns out to be 81.
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, find_multiple
expects two arguments:
The first (named multiplier
) should be an integer,
while the second (named nums
) should be a list of
integers.
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 multiplier
parameter variable as 5
and the nums
parameter variable as [2, 3, 5, 8, 13, 21, 34, 55, 89]
.
We begin the function's body, in this case entering the
loop with n
being the first value in nums
, which is 2.
Since 2 % 5 is 2, we do not enter the if
statement.
We go for another iteration,
with n
being the second value in nums
, which is 3.
Since 3 % 5 is 3, we do not enter the if
statement.
We go for another iteration,
with n
being the third value in nums
, which is 5.
Since 5 % 5 is 0, we enter the if
statement.
We have now reached a return
statement.
This says to stop the function immediately; we do not go for any
more iterations of the for
loop.
The function's value is the value following the word return
;
in this case we find n
, whose value is 5, so
the value of
“find_multiple(5, [2, 3, 5, 8, 13, 21, 34, 55, 89])
”
turns out to be 5.
Notice that since the function stopped as soon as it reached
a return
statement, it never discovered that 55 is also
a multiple of 5.
And what if we gave find_multiple
a list where it
never reaches a return
statement, as in
“find_multiple(3, [1, 2, 4, 8, 16])
”?
Once Python “goes off the end” of a function's body,
it simply returns with a special value of None
.
As it happens, that seems a reasonable value to return for this
problem.
However, what if we wanted find_multiple
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 return
statement outside the loop.
def find_multiple(multiplier, nums):
for n in nums:
if n % multiplier == 0:
return n
return 0
This function has two return
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.