[Tutor] Focus on the functions [Was Re: if-else statements]

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Mon Oct 17 05:32:46 CEST 2005



> Having studied the examples given here, I am beginning to get some
> understanding of what is going on. If only I could find many more
> examples like these, in simple English, I would be a very happy student.

[Note: this is a fairly long post.  Didn't know how to do the subject
justice without really doing some work.  *grin*]

Hi Norman,

I'm a concrete-examples sort of person, so I'll take an example from
another tutorial and see if I can talk about functions in that context.

Ah, good, there's an 'area.py' example in Josh Cogliati's "Non-programmers
Tutorial For Python" that looks ripe.  It's near the bottom of:

    http://www.honors.montana.edu/~jjc/easytut/easytut/node9.html

Let's tear it apar... er, I mean, let's do some constructive criticism.
*grin*


For starters, here's what it looks like (with some small edits):

#################################################################
def hello():
    print 'Hello!'

def area(width,height):
    return width*height

def print_welcome(name):
    print 'Welcome,',name

name = raw_input('Your Name: ')
hello()
print_welcome(name)
print
print 'To find the area of a rectangle,'
print 'Enter the width and height below.'
print
w = input('Width:  ')
while w <= 0:
    print 'Must be a positive number'
    w = input('Width:  ')
h = input('Height: ')
while h <= 0:
    print 'Must be a positive number'
    h = input('Height: ')
print 'Width =',w,' Height =',h,' so Area =',area(w,h)
#################################################################


In this example, the area program uses functions to try to give names to
particular concepts.  The hello() function tries to capture the idea of
saying hello, and print_welcome() is a function that tries to give a
personal welcome to the user.  We also see an area function that takes a
width and height, and returns the area of a triangle of those dimensions.


There are many possible ways of capturing related concepts together in
functions.  For example, we might think that the personal greeting stuff
with hello() and print_welcome() should really be considered bundled
together as a single concept.  We might do something like this:

######
def print_personal_hello():
    name = raw_input('Your Name: ')
    hello()
    print_welcome(name)
######


And if we make this function, then we can change our program to use this
new function:

######
print_personal_hello()
print
print 'To find the area of a rectangle,'
print 'Enter the width and height below.'
print
w = input('Width:  ')
while w <= 0:
    print 'Must be a positive number'
    w = input('Width:  ')
h = input('Height: ')
while h <= 0:
    print 'Must be a positive number'
    h = input('Height: ')
print 'Width =',w,' Height =',h,' so Area =',area(w,h)
######


If we take a revised look at this program, we might also think that the
initial instruction print statements should be boxed too as a
print_instructions() function:

######
def print_instructions():
    print
    print 'To find the area of a rectangle,'
    print 'Enter the width and height below.'
    print
######

Again, we're writing a function so that we can express the idea that those
print statements belong together.  Because I personally think they're
related conceptually, I'm expressing that idea by boxing them with a
function.


Let's see what the program looks like now with those two new functions:

######
print_personal_hello()
print_instructions()
w = input('Width:  ')
while w <= 0:
    print 'Must be a positive number'
    w = input('Width:  ')
h = input('Height: ')
while h <= 0:
    print 'Must be a positive number'
    h = input('Height: ')
print 'Width =',w,' Height =',h,' so Area =',area(w,h)
######


I think you can guess what I'm looking at next.

To an experienced eye, cut-and-pasted code is really ugly and a prime
target for functions, so we're going to go after that next.  This
particular part of the program tries to input positive integers for width
and height.  Let's look at the width-grabbing code:

######
w = input('Width:  ')
while w <= 0:
    print 'Must be a positive number'
    w = input('Width:  ')
######

This block of code is trying to get a positive number.  We can do an
initial revision of this code by doing this:

######
def read_width():
    w = input('Width:  ')
    while w <= 0:
        print 'Must be a positive number'
        w = input('Width:  ')
    return w
######

If we look at this a little closer, though, we can still see duplication:
that 'Width:' string is repeated twice, and also repeated in the name of
the function as read_width.  So perhaps it might be useful to make the
function a little more general by making it take the dimension as a
parameter.  Let's take read_width() and turn it into read_dimension():

#######
def read_dimension(dim):
    d = input(dim + ':  ')
    while d <= 0:
        print 'Must be a positive number'
        d = input(dim + ':  ')
    return d

def read_width():
    return read_dimension('Width')
#######


We know that process of reading the height is going to look really similar
to read_width(), so let's express that too:

######
def read_height():
    return read_dimension('Height')
######



What does the program look like now?  Since we've been doing so much
transformation, let's make this self-contained and repeat the function
definitions too, just so we remember what in the world we've been doing.
*grin*

###################################################################
def hello():
    print 'Hello!'

def print_welcome(name):
    print 'Welcome,',name

def print_personal_hello():
    name = raw_input('Your Name: ')
    hello()
    print_welcome(name)

def print_instructions():
    print
    print 'To find the area of a rectangle,'
    print 'Enter the width and height below.'
    print

def area(width,height):
    return width*height

def read_dimension(dim):
    d = input(dim + ':  ')
    while d <= 0:
        print 'Must be a positive number'
        d = input(dim + ':  ')
    return d

def read_width():
    return read_dimension('Width')

def read_height():
    return read_dimension('Height')

print_personal_hello()
print_instructions()
w = read_width()
h = read_height()
print 'Width =',w,' Height =',h,' so Area =',area(w,h)
###################################################################

Let's stop turning things into functions at this point, and review.

The program has expanded a bit!  Some of the functions we've written are
actually a little useless, so if we were to go further, we might consider
undoing some of the "useless" functions by inlining, and that should
reduce the overall lines of the file.

(Of course, what we'd consider to be "useless" is subjective.  I think the
hello() and print_welcome() functions could be inlined into
print_personal_hello().)

But even with that expansion, we can now read the motions of our program
as the following steps:

    print_personal_hello()
    print_instructions()
    w = read_width()
    h = read_height()
    print 'Width =',w,' Height =',h,' so Area =',area(w,h)

We've turned the main program from a bunch of disconnected print
statements and while loops into fairly straightforward "straight-line"
code.  (That last statement looks funny, so I'd personally turn that into
a function called "print_final_report" or something, just to be
consistent.)


In all of this, although we've done some major movement of code around, we
have not added anything in terms of what the program will do.  All the
function refactoring we've been doing should still preserve the original
program's behavior.  We write our programs with functions, then, not for
our computer's benefit, but for our human readers.


I hope this made some sort of sense.  If you have questions, please feel
free to ask!



More information about the Tutor mailing list