[Tutor] how to create a persistent dictionary w/ cpickle?

Steven D'Aprano steve at pearwood.info
Wed Sep 8 23:50:13 CEST 2010


On Thu, 9 Sep 2010 03:43:42 am Carter Danforth wrote:
> Hi, I'm trying to a create a basic addressbook for practice. I'm
> using a dictionary with cpickle, though I'm not sure how to
> persistently store each instance in the dictionary. Below is the code
> I have so far.
>
> If I run it once and add a contact and the details, it's fine.
> p.load(f) shows the details next time I run it, but if I add another
> contact, and run it again, the previous key:value doesn't show. It
> gets replaced by the new one.

Where do you think you are *adding* a new contact? You don't. You 
*replace* the existing contact with a brand new one, every time.

The problem has nothing to do with pickle, or storing "each instance in 
the dictionary". Pickle is already storing each instance in the 
dictionary. The problem is that you never *add* anything to the address 
book, you *replace* it each time, so there is never more than two 
instances in the dictionary (one key, one value).

You don't have an address BOOK, you only have a single address.


> How can I fix this so that it adds the new key:value to the
> dictionary instead of replacing the existing one? Appreciate any
> help, thanks.

I would dump the entire address class for now and just go for something 
nice and minimal. Get that working first, and then, *if necessary*, 
wrap it in a class. This is Python, not Java -- we use whatever works, 
and don't force everything to be a class when it doesn't have to be.

What's the simplest address record you might have? How about a name 
linked to a telephone number and email?

address_book = {name: (tel, email), another_name: (tel, email), ...}

So, here's the basic algorithm:

(1) Look for the address-book. If it doesn't exist, create an empty 
dictionary, and pickle it as the address-book.

(2) Read the address-book from the pickle file. It will be a dictionary, 
possibly empty.

(3) Add an address to the dictionary. Don't create a new dictionary:

>>> addresses = {}  # creates a new, empty address book
>>> addresses["Fred"] = ("1234 5678", "fred at example.com")
>>> addresses["Betty"] = ("2468 1357", "betty at nowhere.com")
>>> addresses  # not empty any more
{'Betty': ('2468 1357', 'betty at nowhere.com'), 'Fred': ('1234 
5678', 'fred at example.com')}

(3) Save the dictionary to the pickle file.


Once you have those steps happening manually, then wrap it into a class 
so they happen automatically.


Some more comments on your code:


> import cPickle as p

Now that's just lazy. While laziness in a programmer in a good thing, 
this is taking it to extremes!!! You use pickle twice, three times if 
you count the import. Is it really such a burden on you to 
type "cPickle" (or "pickle") two more times, that you need to rename 
it "p"?

Excessive use of one-character names is one of the worst programming 
habits you can have. Don't do this.


> addressbook = 'addressbook.data'

Misleading variable name. "addressbook" isn't an addressbook at all, 
it's a filename.

> f = file(addressbook, 'r+')

You shouldn't keep the file open for large periods of time. On Windows, 
it may mean that it will be locked from any other program accessing it 
during that time, and it risks data corruption if your program crashes 
or the power goes out.

Open and close the file just when you need it.


> class address:

A minor point: it is the convention in Python that (most) classes start 
with a capital letter. So Address would be the class, leaving address 
available for an instance of the class:

address = Address()


>     def __init__(self, name, tel, email):
>         self.name = name
>         self.tel = tel
>         self.email = email
>         ab = {self.name : self.tel}
>         f = file(addressbook, 'r+')
>         p.dump(ab, f)
>
> print p.load(f)
> x = raw_input()
>
> if x == 'add':
>     name = raw_input('\nName: ')

To get a blank line before the prompt, it might be nicer to do this:

print
name = raw_input('Name: ')

That's a matter of personal preference though.


>     tel = raw_input('Tel: ')
>     email = raw_input('Email: ')
>     contact = address(name, tel, email)




Hope this helps,



-- 
Steven D'Aprano


More information about the Tutor mailing list