How can I isolate a Python dictionary from a list for comparison?
Question: I have a Python function that takes as arguments a player's name and score and
determines whether this is the player's highest score. It does so by comparing
the arguments against a shelve object.

The shelve should only store the high score for each player; there should not
be to scores for someone named "Joe" for example.

Unfortunately, I can't figure out how to isolate a dict from the list of dicts
(shelf) for comparison against the incoming player dict.

Here's my code:

    
    
    import shelve
    import os
    
    
    def highscore(player_name, player_score):
        """
        Function to return the high score from our
        persistent storage of score records for a given
        person.
        """
        # Get our working directory
        working_dir = os.getcwd()
        # Create our shelf object for a player
        highscore_fn = os.path.join(working_dir, 'highscore.shelve')
        # Set our player info
        player = {'name': player_name, 'score': player_score}
    
        with shelve.open(highscore_fn, writeback=True) as shelf:
            # Check if any records exist in the shelf
            if len(shelf) == 0:
                # Assign the shelf to an empty list
                shelf['player_data'] = []
                # Append player data to shelf
                shelf['player_data'].append(player)
                # Current high score for player
                high_score = player.get('score')
            else:
                # Loop through our player data list
                for data in shelf['player_data']:
                    # Check to see if we have data for a player
                    if player['name'] in data['name']:
                        existing_record = data
                        # Compare the player's new score against previous score
                        if player.get('score') > existing_record.get('score'):
                            high_score = player.get('score')
                            # Update our record for the player
                            existing_record.update(player)
                        else:
                            high_score = existing_record.get('score')
                    else:
                        high_score = player.get('score')
                        shelf['player_data'].append(player)
    
        # Return the high score
        return high_score
    

Any tips would be appreciated!


Answer: If you don't have another reason to use a list of dicts, just using a dict of
dicts (or even a simple dict) would greatly simplify your code. Assuming your
shelf looks something like

    
    
    {
       "user_data": { 
          "joe": {"score": 2999, "name":"joe"}, 
          "walt": {"score": 1784, "name":"walt"}, 
          "bunny": {"score": 87441, "name":"bunny"}, 
          # etc
          },
    }
    

Then your code would look like:

    
    
    player = {'name': player_name, 'score': player_score}
    high_score = player_score
    with shelve.open(highscore_fn, writeback=True) as shelf:
        user_data = shelf["user_data"]
        # Check if any records exist in the shelf
        found = user_data.get(player_name)
        if found:
            if found["score"] < player_score:
                user_data[player_name] = player
            else:
                high_score = found["score"]
        else:
            user_data[player_name] = player
        shelf["user_data"] = user_data
    
    return high_score
    

Note that if the shelf only contains "user_data", you can get rid of this
level and directly store your dicts in the shelf itself. Also if you only have
scores to save, you can turn your dict of dicts into a simple dict, ie:

=> your shelf:

    
    
    {
      "joe": : 2999, 
      "walt": 1784, 
      "bunny": 87441, 
      # etc
    }
    

=> your code:

    
    
    high_score = player_score
    with shelve.open(highscore_fn, writeback=True) as shelf:
        # Check if any records exist in the shelf
        found = shelf.get(player_name, 0)
        if found > player_score:
            high_score = found
        else:
            shelf[player_name] = player_score
    
    return player_score
    

EDIT: The following code JustWorks(tm) on 2.7.3:

    
    
    # scores.py
    import shelve
    
    DATA = {
        "user_data": { 
            "joe": {"score": 2999, "name":"joe"}, 
            "walt": {"score": 1784, "name":"walt"}, 
            "bunny": {"score": 87441, "name":"bunny"}, 
            # etc
            },
        }
    
    
    class Score(object):
        def __init__(self, path):
            self.path = path
    
        def init_data(self, data):
            shelf = shelve.open(self.path)
            shelf["user_data"] = data["user_data"]
            shelf.close()
    
        def read_data(self):
            d = {}
            shelf = shelve.open(self.path)
            d["user_data"] = shelf["user_data"]
            shelf.close()
            return d
    
        def highscore(self, name, score):
            player = {'name': name, 'score': score}
            high_score = score
            shelf = shelve.open(self.path)
            user_data = shelf["user_data"]
            found = user_data.get(name)
            if found:
                if found["score"] < score:
                    user_data[name] = player
                else:
                    high_score = found["score"]
            else:
                user_data[name] = player
            shelf["user_data"] = user_data
            shelf.sync()
            shelf.close()
            return high_score
    
    
    >>> import scores
    >>> s = scores.Score("scores.dat")
    >>> s.init_data(scores.DATA)
    >>> s.read_data()
    {'user_data': {'walt': {'score': 1784, 'name': 'walt'}, 'joe': {'score': 2999, 'name': 'joe'}, 'bunny': {'score': 87441, 'name': 'bunny'}}}
    >>> s.highscore("walt", 10000)
    10000
    >>> s.read_data()
    {'user_data': {'walt': {'score': 10000, 'name': 'walt'}, 'joe': {'score': 2999, 'name': 'joe'}, 'bunny': {'score': 87441, 'name': 'bunny'}}}
    

