How am I doing?

Andrew Durdin adurdin at gmail.com
Mon Sep 19 21:08:49 EDT 2005


On 9/20/05, Jason <jason at jasonmhirst.co.uk> wrote:
> class HiScores:
>      def __init__(self,hiScores):
>          self.hiScores=[(entry[:5],entry[5:]) for entry in hiScores]

In your original code, you were using slicing to extract the first
five digits (being the score) from the string; now that you're using
tuples of (score, name), you don't need the slicing at all. In fact,
the slicing here is not doing what you expect. If we consider a single
instant in this list comprehension, then this is doing the equivalent
of:

    entry = ('10000','Alpha')
    a = entry[:5]        # ('10000', 'Alpha'), as entry only has two items
    b = entry[5:]        # (), as entry has only two items
    new_entry = (a, b)

So the entry in self.hiScores corresponding to this would be a tuple
(('10000', 'Alpha'), ()).  When (in showScores()) you use this with %
name, score  -- name is the first tuple ('10000', 'Alpha'), and score
is the second ().  The % operator takes (always!) a single tuple, and
finds the '10000' and 'Alpha' to match the two "%s"; then score is
just printed afterwards, which is just () here.

Since all you want to do here in __init__() is copy the input, you
could use the following:

    self.hiScores=[(entry[0],entry[1]) for entry in hiScores]

Or, using Python's tuple-unpacking, you can get a clearer version:

    self.hiScores=[(score, name) for score, name in hiScores]

But since tuples are immutable, it is generally safe to grab another
reference to a tuple instead of copying it; so we can just copy the
whole list:

      def __init__(self,hiScores):
          self.hiScores=list(hiScores)

I'd also recommend that you use ints, not strings, to store the
scores; when printing the scores, you can use the formatting options
of %d to display the score with the appropriate number of zeroes; or
you could use str(score).zfill(5) if it makes more sense to you (I
haven't given you an example of this change).

% substitution has its own trap, in that the , operator has lower
precedence than %, so whenever you want to use more than one value
with %, you need to wrap them all in parentheses:

      def showScores(self):
          for name,score in self.hiScores:
              print "%s - %s" % (name,score)

>      def addScore(self,score,name):
>          score.zfill(5)
>          bisect.insort(self.hiScores,(score,name))
>          if len(self.hiScores)==6:
>              self.hiScores.pop()

If you use ints to store your score, you can remove the line calling
.zfill() here.
You'll need to import the bisect module before you can use it. Note
that bisect.insort() will not work as expected if the list is not in
sorted order. You should make sure the list is sorted in __init__.

> def main():
> 
> hiScores=[('10000','Alpha'),('07500','Beta'),('05000','Gamma'),('02500','Delta'),('00000','Epsilon')]
> 
>      a=HiScores(hiScores)
>      print "Original Scores\n---------------"
>      a.showScores()
> 
>      while 1:
>          newScore=random.randint(0,10000)
>          if newScore.zfill(5) > a.lastScore():

> I've read as many websites as I can about zfill and I can't see why on
> earth it's failing.

random.randint() returns an int; zfill() is a method of strings.  If
you use ints for the score (as recommended above), then you need only
compare newScore > a.lastScore(); if you want to continue using
strings, then you'll need to use str(newScore) to get a string.

 
> 2) The output of the predefined hiscores is now...
> 
> Why are there the pairing parenthesis there?  George very kindly showed
> me another way which was to have...

I've tried to explain why this is occurring above -- it's due to a bug
in __init__(), and another in showScores().
 
> def showScores(self):
>            for entry in self.hiScores:
>                print entry[0:5]," - ",entry[5:]
> 
> But using that method output the entire list in it's full format (sorry
> if that's not the correct terminology).  But give me a small plus mark
> for changing code and not simply copying George :)

No, you made almost exactly the right change to showScores() needed
because of using tuples to store the name and score. You only tripped
over the  % name, score  instead of  % (name, score)  problem that
almost everyone makes (I still do it regularly).

                                                                      
                                                    >
> 3) The hardest thing to 'understand' is the line...
>         self.hiScores=[(entry[:5],entry[5:]) for entry in hiScores]
> 
> I 'understand' what it's doing, but I don't quite comprehend what the :5
> and 5: do.  I know that the :5 is technically saying from the start to
> position 5, and likewise the 5: would say from position 5 onwards, but I
> just can't get my head around how this works.

See my explanation of __init__().

Does that help?

Andrew



More information about the Python-list mailing list