Newbie Nested Function Problem

Terry Carroll carroll at tjc.com
Mon Jan 26 15:53:44 EST 2004


On Sun, 25 Jan 2004 03:47:17 GMT, "Brian Samek"
<zensunni at rochester.rr.com> wrote:


>I don't understand what you're saying.  I designed the program as three
>functions nested within each other.  Are you saying I should be designing it
>differently?  

Yes.  There are some sometimes good reasons to have a recursive function
(i.e., a function that calls itself), but they don't seem applicable here.

> I made it a series of nested functions because, for example, I
>needed to use the "number" variable from ask_number before it was destroyed.
>When a function ends, any variables it has created are destroyed.

The standard way to do that is to return the variable to the caller, which
then uses it.

Here, let me give you an example of how to approach something like this.
I'm going to try to re-use as much of your logic code as possible, so we
can concentrate on the structure rather than the detail pieces.  In
practice, I'd code it somewhat differently, but I don't want to go there
right now.  (We can talk about that after we get the structure down, if
you like.)

You basically have four elements needed here:
 1) an "ask_number" function to find out the number entered by the user;
 2) a "countdown" function to do the countdown;
 3) a function to ask if the user wants to repeat or leave
    (One change I made here is the name; I'll discuss that in a second);
 4) a main routine that puts these all together.

Here's an example of how to do the first function, ask_number:

def ask_number():
    answerOK = False
    while not answerOK:
        number = input("Please enter a number.\n")
        if number > 500 or number - int(number) != 0 or number < 1:
            print "Input positive integers less then 501 only, please."
        else:
            answerOK = True
    return number

The variable "answerOK" controls whether you have a legitimate answer or
not.  It starts out false, but goes true when the user answers with a
number that meets your requirements.  Once it's true, it returns that
number to the caller.

So the caller can just say:

 x = ask_number()

and he's pretty much assured to get a number meeting your specification,
i.e., between 1 and 500.

Again, I'd use a different test for the if statement and a few other
things, but I wanted to preserve as much of your approach as possible.

Now, let's move on to element 2, the countdown.  Your code pretty much
basically works as is:

def countdown (number):
    while number != 0:
        print number
        number = number - 1

There are more Pythonic ways to do this, but let's leave this be.

Now, element 3, the function to ask if the user wants to repeat or leave;
I've made a trivial but important (I think) change here.  You called it
"leave", but the user types 'y' if he *doesn't* want to leave, and 'n' if
he does want to leave.  It makes more sense to describe (and name) the
function not in terms of whether the user wants to leave, but rather
whether he wants to continue.  Also, since the function doesn't actually
do the leaving (or continuing) but just asks the question and finds out, t
makes sense to name it something that reflects that (as you did with
"as_number").

So, I've renamed the function to "ask_continue" instead of "leave".

Again, I'd code this a little differently, but leaving as much of your
code intact, you can come up with something like this:

def ask_continue():
    answerOK = False
    while not answerOK:
        continue_answer = raw_input ("Type 'y' to start over - type 'n' to
exit. ")
        if continue_answer  == "y":
            answerOK = True
        elif continue_answer == "n":
            answerOK = True
        else:
            print "Type either 'y' or 'n' please."
    return continue_answer

Same idea here; "answerOK" is a variable that indicates whether you've
gotten a legit answer, and once you do, it returns it.

Now, to put it all together, you need that fourth element, the main
program that uses all of these.  Now that you've got the blocks, the main
program is pretty straightforward:


def main():
    repeat = "y"
    while repeat == "y":
        limit = ask_number()
        countdown(limit)
        repeat = ask_continue()

This is basically a big loop that repeats until the value of "repeat" is
something other than "y".

The end result here is that, when you're working on the main program, you
can really forget about how the functions work internally.  All you have
to remember is what each one returns.  And, as you work on each function,
you don't have to worry about the other functions at all.  When you are
writing "ask_number" for example, you can make as many changes to it as
you want, and never have to fear that you'll break something in
"countdown," or in the main program.  And, if you later write another
program that needs a prompt for that type of number, you can just copy
this function out and use it.

These two features, code reuse and code isolation, are the major reasons
to use functions.  With the approach you originally posted, the code of
the functions are so interwoven, that you don't get either.  You can't
work on that version of your program other than as a big mass.  By
breaking it down, you've got a bunch of small easy-to-solve problems,
which you can approach individually.

Now, none of the above is gospel, and for each function, there are a lot
of ways to approach it, and Python has some features that would suggest
approaching them in certain ways (what we call "pythonic") that I haven't
done here (because I don't want to throw that at you right now).  For
purposes of this post, don't concentrate too much on how each function
works; look at how the problem has been broken down and how hote solution
has been structured.

Hope this helps.



More information about the Python-list mailing list