[Tutor] Engarde program was: i++

Danny Yoo dyoo at cs.wpi.edu
Wed Jun 6 03:14:57 CEST 2007


> There where some values I did not want saved to the character file.  A couple 
> where values that are for all characters, so I put them into their own class.
>
> ###############################
> class Master_stats:
>     def __init__(self, year, month):
>         self.year = year
>         self.month = month
> ###############################
> There are only two values now, but that will most likely increase as I get 
> further into the program.


Hi Scott,


Sounds good.  Ok, let's re-review the code now.


####################################################################
def mfile_read():
     '''This will open the master file and load all the variables'''
     i = 0
     [some code cut]

     master = cPickle.load(mfile)
     mfile.close()
     return [master.year, master.month]
####################################################################

In the context of the new Master_stats class, it makes more sense for 
mfile_read() to return a Master_stats object now rather than a two-tuple 
list.  You already had instincts about bundling year and month together 
here, which is good, so if you follow through on this, it'll let you 
dissolve a few more lines of legacy code.



###################################################
def char_save(character):
     '''This saves the character to the harddrive'''
     path = "./characters/" + character.player
     cfile = open(path, "w")
     cPickle.dump(character, cfile)
     return
###################################################

To make the code a little safer, explicitely close the file here.  The 
Python language itself doesn't say that 'cfile' here will be closed at the 
end of char_save() --- the fact that it does happen is an implementation 
detail, since Jython does something different! --- so it's better just to 
avoid the possible ambiguity and explicitely close files resources.



Ok, looking at create_char() since it's one of the larger functions.  The 
value of 's' in create_char() is mixed: at one point, it's an integer, at 
other instances, a string.  There's a possible flow of control that 
doesn't make sense to me.

##########################################################
     s = 0
     while s < 1 or s > 3:
         print 'Is', player, 'the player you wanted?'
         s = raw_input()
         if s == 'y' or s == 'yes':
             break
         elif s == 'n' or s == 'no':
             return
         else:
             print '\nplease select y, n, yes, or no\n'
     while s < 1 or s > 3:
         ## code cut
##########################################################

The condition at the last line there should be a type error: 's' at the 
end is a string, and strings should not be comparable to numbers. 
Unfortunately, Python suffers a defect here: it is way too lax in this 
area, so something consistent (but meaningless!) is going to happen here.


Also, around this code, I see I lot of code duplication.

##############################################################
     while s < 1 or s > 3:
         print '\nIs', name, 'a sufficient character name?'
         s = raw_input()
         if s == "y" or s == "yes":
             break
         elif s == "n" or s == "no":

             while s < 1 or s > 3:
                 print '\nIs the alternate name', alt_name,
                 print 'a sufficient character name?'
                 s = raw_input()
                 if s == "y" or s == "yes":
                     name = alt_name
                     break
                 elif s == "n" or s == "no":
                     return
                 else:
                     print '\nplease select y, n, yes, or no\n'

             break

         else:
             print '\nplease select y, n, yes, or no\n'
##############################################################

The duplication is the logic of first prompting a question, then repeating 
the question until we get a straight yes-or-no.  The type mistake with 's' 
is probably a side effect of a bad copy-and-paste.  Let that be a lesson 
to you!  *grin*


But the fix isn't just to fix the type error everywhere: the real fix is 
to get rid of the copy-and-pasting altogether.  Make a function to handle 
the asking of yes/no style questions.  Write a function called is_yes() 
which takes in a question string, and returns True or False.  If we put on 
our "wishing makes it so" hat, here's what we'd like:

##################################
>>> is_yes("did you like this?")
did you like this?
huh?
please select y, n, yes, or no
I don't want to
please select y, n, yes, or no
oh all right
please select y, n, yes, or no
y
True
##################################

So is_yes() should be written to keep asking, and at the end, return True 
if the user affirmed the choice, and False otherwise.


If you have an is_yes() helper function, then the logic of asking all 
those questions dissolves down to:

##############################################################
if not is_yes("Is " + name + " a sufficient character name?"):
     if is_yes("Is the alternate name " + alt_name +
               " a sufficient character name?"):
          name = alt_name
     else:
         return
...
##############################################################

This should make the logic of name selection much clearer than it is right 
now.  At the moment, it's cluttered with the other auxiliary logic you're 
using to make sure the user types in "yes" or "no", and it makes it hard 
to see the intent of the code.


If you have questions about any of this, ask, and one of us here on Tutor 
will be happy to talk about this more.


More information about the Tutor mailing list