|
|
CS 1110 - Introduction to Programming
Starting Out with Python
Chapter 5, Functions, part 1
Objectives:
This lesson covers the first half of chapter 5, which
introduces creation of functions. Objectives important to this lesson:
- Introduction to functions
- Void functions
- Designing a program with functions in mind
- Local variables
- Passing arguments
- Global variables and constants
- Value-returning functions
Concepts:
Chapter 5, part 1
Chapter 5 begins with a definition of what a function is. This
could have come in any of the chapters so far, since we have been using
built-in functions in all of them. Let's look at at the definition.
A function is a group of statements that perform a specific
task. I have shortened the book's definition, because we have
already seen that some functions do not "exist within a program". They
can exist outside your program, and still be used in your program. We
will explore how that happens as we go along.
In figure 5-1, the text shows us two approaches to writing a
program. We could write the program as a long series of steps.
We already know how to modify that approach by using loops and
selection, but lets grant that a program with selection structures and
loops still kind of flows straight down the page, only with some jumps
and some loops.
The second approach takes us to a new idea. Imagine the
payroll program with more features, such as calculating tax and with
withheld amounts for benefits. What if we had a separate function for
each of the features: one to collect employee ID, another to collect
hours, another to calculate regular gross pay, another to calculate
overtime (which would only run when there is overtime), another to
calculate federal tax, another for state tax (which would vary by the
states where our employees work), and more. There would have to be
logic in the program that decides which functions we need to use for
the employee whose data is being processed. This approach is modularized
programming. We can create the separate functions we need, and only run
the ones that apply to the current job we are doing.
We get a sales pitch on the next page that explains benefits
of using a modularized approach:
- simpler code - writing and troubleshooting each function
can be simpler than trying to find a problem in a long, complex program
- code reuse - having functions in one program that can be
copied and pasted into another is a time saver, as long as the copied
code is what we need, or is close enough that it can be easily modified
- better testing - some of the programs we have written so
far could have been easier to test if we used a modular approach,
testing each module as we wrote it; this can be done with standard
programming, but it is more difficult
- faster development and facilitation of teamwork - if a team
of five, for example, is working on a set of five programs that all
need to perform some similar tasks, it makes more sense to assign
writing the tasks to various team members, who could then share their
code with each other, then the team members could proceed separately on
the parts of the programs that are different from each other
The next section of the text does not explain its concept well
enough. You already know that we can pass an argument to some functions. We have done
so with every print() function we have used so far. You need to know
that functions can also return
things to the line of code that calls them. Not all functions return
anything. Those that do not are called void functions. Those that do
are called value-return functions. They have to be written so they know what value they return, which can be any type of data: int,
float, string, or other types. We will begin by writing new void
functions.
Void Functions
When we create a function, we begin by creating its definition,
which is really writing the code it will use and giving it a name.
Functions have to be defined before they are called, and they
have to have a name to call them by. The text lists several rules about
naming functions in section 5.2.
- Don't use a keyword as a function name.
- Don't use spaces in a function name.
- Like a variable name, the first character of a function
name must be a letter (upper or lower case) or an underscore. The rest
of the name can be a combination of letters, underscores, and numerals.
- Python cares about the capitalization you use in function
names. (That's not a surprise, is it?)
- This is a guideline, not a rule. Give your functions
meaningful names, or you are likely to forget what they are supposed to
do.
When you create a function, you need its name as the second
thing you type. The first thing you type is the keyword def,
which stands for definition. A function definition in Python will look
like this:
def function_name():
statement
statement
statement
This looks like other code structures we have used. There is a
colon, and there are statements that are indented. That first line, the
one that is not indented, is the function header. It has four
parts that are all required: the keyword def, the function
name, the set of parentheses, and the colon. The
only part you have any say about, at this time, is the function name.
(More secrets are coming...)
The text gives us an example of a new function that prints two lines.
To run that function, or any
function, we need a code statement that uses the function name like a
command, like this:
function_name()
This is a function call.
When we call a function, we
have to include the parentheses
as part of its name, but not the colon that we used in the definition.
If the function expects to receive an argument, it goes inside the
parentheses, just as you have always done when you called the print()
function.
Figure 5-2 shows us how a program having this function could
work. It has two comments at the top of the program file, followed by
the function definition. There is another comment, then the line that
calls the function. It is important to understand that when this
program runs, the first thing
that happens is that the definition of the function is loaded into
memory, so it can be called. The first line that is actually executed in this program is the one
that calls the message() function.
Modularized Programming
The next example is Program 5-2, which introduces another
refinement. This program adds a new function to our example, called the
main() function. In a
modularized program, the main() function is the control center for your
program. All program flow should happen in the main() function, which
calls any other functions in the program as needed. If this were a
program written in C, the main() function would run automatically when
the program runs. Note that in the Python code examples in this part of
the chapter, the main() function has to be called by a separate line of
code, shown as the last line in each of these examples.
The text changes gears to discuss flowcharting
a program with functions. In figure 5-9, the text shows us a flowchart
for the program we have been discussing. Each of the two functions is charted separately, and the main()
function contains a call to the message() function. On the next page,
we see a hierarchy chart,
which shows a different idea. It shows all
the functions that exist in a program, linked in a vertical hierarchy.
The links indicate that the functions in lower rows are called by the
functions that link to them from above. In the illustration, the main() calls five other functions.
Two of those five each call two other functions. The other three
functions called by main() do not call other functions. Note that the
hierarchy chart only shows the calls-called-by relationships. It does
not show any code.
In section 5.4, the text tells us that variable used inside a
function are usually local variables. This means that variable created
inside a function are only available to that function, and are not
available to the rest of the program. Program 5-4 is a program written
by someone who does not understand this idea.
- In the main() function, the get_name() function is called.
- In the get_name function, the user is asked for their name,
and it is saved in the variable called name.
- Back in the main() function, the print() function is
called, and handed a literal and the variable called name. This is
impossible, because the only variable called name exists only in the
get_name function. The main() function has no access to this variable.
- There are solutions for this, but we won't handle this
problem yet.
Local Variables
The text calls the part of a program in which a variable may
be seen and used the variable's scope.
If statements using that variable are in the same scope, the statements
will work. If they are not in the same scope, the statements can't
work. The text points out that even if statements using the variable
are in the same scope, they won't work if the variable has not been
created yet when the computer tries to execute those statements.
The text points out that the same variable names can be used
in different scopes without violating any rules. Each of those same
named variables would exist in its own scope, and none would know the
others existed. This is a thing that it is possible to do, but it is
probably a bad idea. It is too easy to become confused about where a
particular variable can be accessed.
So, if variables are local
by default, we need a way for functions to receive arguments. You have
already used some of those, but you have not seen what makes them work.
Turn to section 5.5 to learn an answer to this problem.
Passing Arguments
When we define a function, we don't
have to leave the mandatory parentheses empty. Putting a local variable name
inside the parentheses creates a function that can receive an argument.
Such a variable is called a parameter.
The text illustrates the concept with an example:
def show_double(number):
result = number
* 2
print(result)
In this example, the function
show_double() is created, and it expects to receive a value that it will store
in the parameter variable
called number. Inside the
function, a new variable
called result is created and
assigned the value of number times 2. The function then prints the value that is stored in
result. Read through program 5-6 to see an example using this method.
Like any other variable created and used inside a function, a
parameter variable is limited in scope to the function that uses it.
Some students get confused at this point. They start believing that the
parameter variable exists in both the calling function and the called
function. It only exists in the called function. The calling function
passes a value (which may be stored in its own variable) to the called
function. The name of the parameter variable that the called function
uses to catch the value is unknown and unimportant to the calling
function.
Well, programmers liked passing values to parameter variables
so much that someone had to develop a way to pass multiple values at
once. Program 5-8 illustrates how to do this with two literal values.
It
simply passes two values separated by a comma and a space. The
receiving function has two parameters in its parentheses, separated by
a comma and a space. It should be obvious that you can pass as many
values as you want, as long as the called function has a matching set
of parameters with which to receive those values. You can pass literal
values, or the values held in variables. Just be aware that if you put
the name of a variable in your argument list, you are only passing what
that variable currently holds, not the variable itself.
We are almost halfway through the chapter, so hang on a bit
longer.
Program 5-11 shows us something interesting. When we pass a
value to a function, the function has to catch it in a variable. As the
programmer, you are allowed to know the name of that variable, which is
local to the function receiving the value. Why did I mention that?
Watch what the author does in this one:
# first the author calls a function called show_interest, and
he specifies what value is passed to which parameter variable
show_interest(rate=0.01, periods=10, principal=10000.0)
# the first line of the definition of the show_interest() function
looks
like this:
def show_interest(principal, rate, periods):
The author could
have passed the three values to the called function in the order the
function expected to receive them, eliminating the need to specify
parameter variable names. Why do it this way?
- This method makes it
easier to troubleshoot because it makes explicit assignments to the
parameter variables the called function uses.
- This method can be used
by a team of programmers when the called function has not yet been
written, but has been designed, allowing the completed function to work
in the program as long as its programmer does not deviate from the plan.
So what was remarkable about that? The calling function used
the names of local variables in the called function. That should strike
you as something that should not work. Oddly enough, it does work. You
might think of it as referencing a variable that exists elsewhere, kind
of like putting an address on an envelope before you mail it. The
variable name is never actually used in the calling function. It is
only used by the called function for delivery of data to its variables.
Global Variables and Constants
Okay, so now that we have you worried about local variables,
let's see what we can do when local variables just won't get the job
done. We can create a global variable.
In this case, global means
"throughout the current program". A global variable is accessible to
all functions that are part of your current program. There are two
tricks to it, but you have been doing one of them all along in your
assignments. Look at programs 5-13 and 5-14 in your text. In program
5-13, the trick is to create a variable, but don't create it inside a function.
It is best to create it at the beginning of your program file, before
you define any functions. That's what you were doing with variables
when you didn't know about functions.
- If you create a variable inside a function, it is a local variable.
- If you create it in code that is not inside a function, it is a global variable.
The second trick is shown in program 5-14. This program
does not
create a variable, it references one that was created according to the
instructions above, in the program itself, not in a function. It says
to the function, this is a global variable, not one of our locals. Look
for it outside the function, and use it.
def function_name()
global number
# global is the keyword, number is the variable name. this is how your function gets access to the global variable named number.
The text warns us that we should assign a value to the
variable right away, which is what happens in the next line in program
5-14: the user is asked to enter a value. Then, the program calls a
function that uses the value in the variable. Yes, the function call
could have passed a value to the function, but then the function would
have to be defined as receiving a value. This way is simpler, but dangerous. If all functions have
access to a variable, they can all redefine what that variable holds.
As the text explains, this makes troubleshooting problems with
that variable harder, and it makes functions that use such a variable
unfit to be reused by other programs that don't have an identical
variable.
After complaining about the drawbacks of global variables, the
text tells us about global constants.
In the example program 5-15, a variable is created in the body of the
program, outside of any function, making it global. This variable is named in all capital letters, making it a
constant by convention. A
programmer understanding this convention will know NOT to make any
changes to the value of this variable elsewhere in the program. This is
the best approach we have in Python to creating a global constant. It
is actually a global variable, but we should agree not to change its
value.
Value-returning Functions
The next to last section we will discuss this week is section
5.7, about creating functions that
return values. To illustrate the idea, we are introduced to a
function that returns a value. When you call a function of this sort,
you usually catch the returned value in a variable, so the value can be
used in other statements in your program. The text reminds us that we
have called several functions in our programs so far, and it reveals to
us that all of those functions are part of the interpreter itself.
There are other functions that
we can use that are stored in other files called modules. To use a function in one of
those files, our program must load the module with a simple command.
The keyword import is followed
by the name of the module to be loaded. The module we will use in this
example is called random. It
contains several functions that deal with random numbers.
The command we will use to load the module is:
import random
Lines like this one should be the first lines in your program,
before you have a chance to use any functions, especially any functions
included in the library modules that you are importing.
The text introduces a new notation that we will use to call
functions in a loaded module. The text explains that the function we
are about to call is not in the usual place the interpreter finds
functions, so we have to tell it to look for a function called randint,
which is in the random module. The code that illustrates this has more
to it:
number = random.randint(1, 100)
Reading from left to right:
- the first thing we
see is a variable that will
catch/hold/remember the result of the function we are about to call.
the variable is called number
- the second thing
we see is an equal sign, which
is just an assignment operator
- the third thing we
see is the name of the loaded module
that contains the function we are going to use
- the name of the module
is immediately followed by a dot,
which is followed by the name of the function. the text calls this dot
notation. the simple version of this is that the function is a member
of, a part of the module, so the dot notation tells the interpreter
where to look, and what to look for
- finally, we see a set of parentheses, containing the arguments we are passing to the
randint() function. in this case, the arguments are 1 and 100, meaning
that we want the function to pick a number at random from the range 1
through 100
That's a lot of cargo for one little example. In program 5-16,
we see the same notation used to select a number from 1 to 10, and in
program 5-17, to select a series of random numbers in a loop. This
brings up a question we should ask ourselves: how do we know where a
loop ends in a Python program? We look for the indentation to change
from right to left. Python loves indentation.
The
text spends good bit of time on random number functions.
That material will be needed at the end of next week, but it is not
needed right now, so let's move on to section 5.8 for a quick
introduction to creating your own value-returning functions. Defining a
function that does a return only requires one more line of code, We can
do it like this:
def function_name():
statement
statement
statement
return expression
This looks like the example I gave you above, but on the last
line we see the keyword return, followed by any legal expression you
want returned, usually something that was calculated in the function,
such as the text shows us in program 5-21, in which we see a function
has been created that adds two numbers together and reports the result
with a return command.
We can start off at this point next week. For this week's
assignment, you only need the material we have hit so far in the
chapter.
|