Search code examples
pythonappendshelvepickle

Appending a value to a key in Python?


if savescores == "y":
        name = raw_input("Enter your name.")
        datfile = filename[0:-4] + ".dat"
        highscores = shelve.open(datfile)
        try:
            highscores[name].append(score)
        except:
            highscores[name] = [score]

If this specific player already has a score, I want to append the new score to the scores he already has, but apparently this doesn't work, because it doesn't change his score at all.


Solution

  • The Shelf object does not detect changes to mutable objects in your shelf. It only detects assignment.

    To fix this, open your shelf with writeback=True and make sure to close when you are done. (You can also sync every so often to lower memory usage of the cache.)

    Relevant docs from shelve.open:

    Because of Python semantics, a shelf cannot know when a mutable persistent-dictionary entry is modified. By default modified objects are written only when assigned to the shelf (see Example). If the optional writeback parameter is set to True, all entries accessed are also cached in memory, and written back on sync() and close(); this can make it handier to mutate mutable entries in the persistent dictionary, but, if many entries are accessed, it can consume vast amounts of memory for the cache, and it can make the close operation very slow since all accessed entries are written back (there is no way to determine which accessed entries are mutable, nor which ones were actually mutated).

    Note that you can skip opening with writeback=True to save memory, but your code will be more verbose as shown in this example from the shelve docs.

    # as d was opened WITHOUT writeback=True, beware:
    d['xx'] = [0, 1, 2]    # this works as expected, but...
    d['xx'].append(3)      # *this doesn't!* -- d['xx'] is STILL [0, 1, 2]!
    
    # having opened d without writeback=True, you need to code carefully:
    temp = d['xx']      # extracts the copy
    temp.append(5)      # mutates the copy
    d['xx'] = temp      # stores the copy right back, to persist it
    
    # or, d=shelve.open(filename,writeback=True) would let you just code
    # d['xx'].append(5) and have it work as expected, BUT it would also
    # consume more memory and make the d.close() operation slower.
    
    d.close()       # close it
    

    By the way, this code

    try:
        highscores[name].append(score)
    except:
        highscores[name] = [score]
    

    is more concisely expressed as

    highscores.setdefault(name, []).append(score)