[Tutor] calculator structuring (was question about branching/goto)

dman dman@dman.ddts.net
Sat, 27 Apr 2002 15:11:30 -0500


--XMCwj5IQnwKtuyBG
Content-Type: multipart/mixed; boundary="ftEhullJWpWg/VHq"
Content-Disposition: inline


--ftEhullJWpWg/VHq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable


Spotacus,

I took your calculator program and restructured it to use functions to
encapsulate the smaller pieces the program is built up from.  I used a
dictionary to achieve the same result as a switch in C and vastly
shortened the main "while" loop.  I also shortened the functions to
handle addition and multiplication while extending them to handle an
arbitrary number of inputs.  (eg 1+2+3+4+5+6+3+4+5+6+9). =20

Do make a note that input() is very bad.  It is a security hole in the
face of malicious users, or users who mistype something.  (eg instead
of entering a number enter  open('c:/a/file/I/want/to/clobber','w')
and watch that file get clobbered).  For someone just learning how to
program it is ok to use it, but when you better understand python
(in particular types and exceptions) use raw_input() instead and
convert the data to the type you are expecting.

Also note that there are a variety of ways the functions could have
been implemented (for example using reduce()) and have various
tradeoffs in terms of that data is present at the end of the function.
There is a tradeoff between having all the numbers to print out (as
you originally did) and how much memory is used as it runs.

I also used string interpolation in some places when printing out
data.  String interpolation is a convenient way of first describing
exactly how you want the output formated and then letting python put
your data in it.  You can do things like specify how many columns some
data will use and how many digits of precision you want.

You'll learn all of these details in time.  Just keep working at it
and ask on the list whenever you run into something you don't
understand.

The restructured script is attached.

-D

--=20

"...In the UNIX world, people tend to interpret `non-technical user' as
meaning someone who's only ever written one device driver."
    --Daniel Pead
=20
GnuPG key : http://dman.ddts.net/~dman/public_key.gpg


--ftEhullJWpWg/VHq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="calculator.py"
Content-Transfer-Encoding: quoted-printable

=20
#Spotacus' "fivecalc" v2.0

import sys


menu =3D """
1: Add
2: Subtract
3: Multiply
4: Divide
5: Exponents
9: Exit

"""


#
# Here we define each of the operations the program will be performing.  It
# improves modularity and reduces clutter in other parts.
#

def wait():
    raw_input( "Press [Enter] " )


def input_count( min , max ) :
    # Prompt the user for the number of inputs they will provide.  Only
    # responses between min and max (inclusive) are allowed.  Use the flag =
value
    # 'None' to indicate no limit.

    message =3D \
          "How many numbers are you going to provide? (min: %s, max: %s) : =
" %\
                    ( str(min) , str(max) )

    while 1 :
        numcount =3D input( message )=20

        # see if the number is in the allowed range
        if (
             ( (min is None) or (min <=3D numcount) )=20
                and
             ( (max is None) or (numcount <=3D max) )=20
            ) :
                # it is, break the loop now
                break

        print "Invalid number."

    # once the loop terminates we have a valid count
    # return it to the caller
    return numcount=20


def add() :
    addnum =3D input_count( 1 , None )
    # start with the additive identity
    sum =3D 0
    for i in xrange( 1 , addnum+1 , 1 ) :
        sum +=3D input( "Enter number %d : " % i )

    print "The sum of the numbers is equal to" , sum=20
    wait()


def subtract() :
    subnum =3D input_count( 2 , None )
    # there is no subtractive identity, I think
    numbers =3D []
    for i in xrange( 1 , subnum+1 , 1 ) :
        numbers.append( input( "Enter number %d : " % i ) )

    difference =3D numbers[0] - numbers[1]

    for num in numbers[2:] :
        difference -=3D num

    print "The difference of the numbers is equal to" , difference
    wait()


def multiply() :
    # generalize this to handle any number of factors

    numcount =3D input_count( 1 , None )=20

    # start with the multiplicative identity
    product =3D 1
    for i in xrange( 1 , numcount+1 , 1 ) :
        product *=3D input( "Enter factor %d : " % i )

    print "The product of the numbers is" , product=20
    wait()


def divide() :
    # if you decide to convert to floats rather than keep separate quotient=
 and
    # remainder values you can extend this like the others to support any n=
umber
    # of divisions
   =20
    div_1 =3D input("What's the first number? ")
    div_2 =3D input("What's the second number? ")
    div_3 =3D div_1 / div_2
    div_4 =3D div_1 % div_2
    print div_1, "divided by", div_2, "is equal to", div_3, "R",

    # here I'll show how to convert ints to floats, and how to ues string
    # formatting
    print  "%f divided by %f is equal to %f" % \
                ( div_1 , div_2 , float(div_1)/div_2 )
    wait()


def pow() :
    base =3D input("What's the base number? ")
    exp =3D input("What's the exponent? ")
    result =3D base ** exp
    print base , "to the power of", exp , "is equal to", result
    wait()


def exit() :
    print "Thank you,", name
    raw_input("Press [Enter] at your leisure ")
    sys.exit()
=20

# Here we define the table (dict) for our "switch" later on
action_table =3D {
    1 : add ,
    2 : subtract ,
    3 : multiply ,
    4 : divide ,
    5 : pow ,
    9 : exit
}



# note that 'name' is a global variable.  that's fine for this small progra=
m,
# but doesn't scale well in large applications
name =3D raw_input("What's your first name? ")

print "Welcome to the calculator,", name
wait()


while 1 :
    print menu
    # Note: input() is very bad (think about security and malicious users).=
=20
    # It is sufficient for a beginner, though.
    action =3D input("What will you do? (1|2|3|4|5|9) ")

    # You can use an if-else ladder here, or as you have commented, a "swit=
ch"
    # is more appropriate

    if action not in action_table :
        print "That action is not known"
        # skip the rest of the loop and prompt again
        # use 'break' and 'continue' very carefully, they _can_ lead to
        # spaghetti code
        continue

    # here's how we do switches in python -- lookup the handler function fr=
om a
    # table (dict)
    handler =3D action_table[ action ]

    # now call the function that is going to do the dirty work
    handler()



--ftEhullJWpWg/VHq--

--XMCwj5IQnwKtuyBG
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iEYEARECAAYFAjzLBfIACgkQO8l8XBKTpRRXkQCfZOJDG8pLgb3rQZUCfjrmBGBd
KTAAn0ChfkYwfz+iM4x5Uz4U/Q8FdtxS
=nv4L
-----END PGP SIGNATURE-----

--XMCwj5IQnwKtuyBG--