Search code examples
pythonmoduleshelve

Why doesn't shelve know when a change has been made to a mutable object?


I'm currently taking an online Udemy course in Python programming and we're covering the shelve module. The professor has shown that for some reason, a shelve has no way to know when a mutable object has been changed like this: (he has explained ways around this as well)

import shelve

# Making a bunch of lists to bind to shelves later
blt = ["bacon", "tomato", "lettuce", "bread"]
beans_on_toast = ["beans", "bread"]
scrambled_eggs = ["eggs", "butter", "milk"]
soup = ["tin of soup"]
pasta = ["pasta", "cheese"]


with shelve.open('recipes') as recipes:
    # Binding each list to a shelve
    recipes['blt'] = blt
    recipes['beans on toast'] = beans_on_toast
    recipes['scrambled eggs'] = scrambled_eggs
    recipes['pasta'] = pasta
    recipes['soup'] = soup

    # Attempting to append 'butter' and 'tomato' to respective lists
    recipes['blt'].append("butter")
    recipes['pasta'].append("tomato")

    for snack in recipes:
        print(snack, recipes[snack])

The output from the for loop shows that the shelves are unchanged and the values 'butter' and 'tomato' weren't appended to their respective shelves.

My online professor has tried to explain why it is so, but I am having trouble understanding. If somebody could try to explain it to me, I would appreciate it.

And if you have the patience, could you explain any terms you might use in your answer that you think I wouldn't understand. I'm still a beginner and Python is the first language I'm learning. Thanks in advance!


Solution

  • Imagine your recipes shelf as a physical recipe book in your kitchen.

    recipes['blt'] = blt writes a recipe in the book for making a dish like blt.

    Accessing recipes['blt'] makes a dish based on the recipe.

    recipes['blt'].append("butter") makes a dish, and then adds butter to it. The recipe in the book doesn't suddenly involve butter; if you wanted to rewrite the recipe, you would have to do that separately.


    That's basically how shelve works. A Shelf object is backed by a data file on disk, and each entry in the file contains instructions (in pickle format) for building an object. Accessing the shelf builds an object based on the stored instructions, but changing the object you built won't change the instructions.

    If you create the shelf with writeback=True, then instead of building a new object on each access to an entry, the shelf will cache the built objects and return the same object again if you access the same entry again. It still has no idea when you change anything, but since it's returning the same object, the changes are still present.

    When you close a writeback=True shelf, the shelf will write new pickles for the objects when the shelf is closed. The new pickles will reflect any changes to the objects. This may involve a lot of unnecessary writing, since the shelf still doesn't actually know when you've changed an object, so it has to re-pickle all of them, even the unchanged ones.