0

Hi there, I'm learning Python and trying to create a command-line Address Book as a dictionary, which is saved to a file using pickle (that's the briefing).

I've coded the add_contact and browse_contact functions, and they both work well. I have added a few contacts (whose names are simply "Test One", "Test Two" ... "Test Ten") plus their emails and phone numbers to the Address Book.

However, when I code the search_contact and modify_contact functions, I'll need to load the file back into a dictionary using pickle.load().

The problem is, as each contact was added one by one to the Address Book, if I simply use the following code, it will only return the first object in the Address Book:

with open("addressbook.data", "rb") as f:
    loaded_contacts = pickle.load(f)
    print(loaded_contacts)

Output:

{'Test One': ('test.one@jdsofj.com', '39893849')}

That's because "Pickle serializes a single object at a time, and reads back a single object." Based on the solution suggested here, I've changed to the following code, so I can load back all the objects into a dictionary called loaded_contacts:

with open("addressbook.data", "rb") as f:
    while True:
        try:
            loaded_contacts = print(pickle.load(f))
        except EOFError:
            break

That seems to work, but the loaded dictionary from the file will have an extra None object at the end, as shown below once loaded_contacts is printed out:

{'Test One': ('test.one@jdsofj.com', '39893849')}
{'Test Two': ('test.two@clajdf.com', '93294798374')}
.
.
.
{'Test Ten': ('test.ten@oajfd.com', '79854399')}
None

Consequently, when I try to search for a name like "Test One" and try to retrieve its value, I will get TypeError: 'NoneType' object is not subscriptable because there is a None object at the end.

Here's the code (it needs more work but you'll get the idea):

with open("addressbook.data", "rb") as f:
    while True:
        try:
            loaded_contacts = print(pickle.load(f))
        except EOFError:
            break

search_name = input("Please enter a name: ")
print(loaded_contacts[search_name])

Here's the error message after I enter a name:

TypeError                                 Traceback (most recent call last)
/var/.../x.py in <module>
     11 
     12 search_name = input("Please enter a name: ")
---> 13 print(loaded_contacts[search_name])
     14 

TypeError: 'NoneType' object is not subscriptable

Not sure what I did wrong, or why there's an extra None object at the end of the loaded dictionary (or how to remove it). I know I could use a list to store all the values as strings, but the briefing is to create a dictionary to hold all the values and use the dictionary built-in methods to add, delete and modify the contacts--and hence my question here.

Edited to update:

Answer to the question (thanks @JohnGordon and @ewong):

In loaded_contacts = print(pickle.load(f)), print() won’t return anything, so assigning a variable to print() will return Noun.

However, simply removing print() won’t fully work—it solves the ‘None’ problem, but loaded_contacts will only be assigned to value of the last iteration of pickle.load(f).

Here's how to iterate all the objects in pickle.load(f) (if they were initially added one-by-one) and then store all of them in a dictionary:

loaded_contacts = {} # create an empty dictionary

with open("addressbook.data", "rb") as f: # open the file
    while True: # looping pickle.load(f)
        try:
            # add each object to the dictionary
            loaded_contacts.update(pickle.load(f))
        except EOFError:
            break # stop looping at the end of the file
jojo
  • 25
  • 5
  • Welcome to StackOverflow. I noticed ```loaded_contacts = print(pickle.load(f))```. ```print()``` doesn't have a return value (so ```loaded_contacts``` is None) – ewokx Apr 19 '22 at 00:57
  • Hi John, I think you have to call 'pickle.load(f)' in a loop if the object in the file was added one by one (not all at once). Otherwise calling 'pickle.load(f)' will only return the first object, and if you call 'pickle.load(f)' again it will return the second object. See [here](https://stackoverflow.com/questions/53462655/python-pickle-load-only-returns-the-first-value-of-the-file) and [here](https://stackoverflow.com/questions/35067957/how-to-read-pickle-file/35068080#35068080). – jojo Apr 19 '22 at 01:22

1 Answers1

1
loaded_contacts = print(pickle.load(f))

This is your problem. You're assigning loaded_contacts to the return value of print(), which doesn't return anything, so it returns None by default.

Do this instead:

loaded_contacts = pickle.load(f)
print(loaded_contacts)
John Gordon
  • 29,573
  • 7
  • 33
  • 58
  • Thank you. This will indeed print out everything from "Test One" to "Test Ten" without the None object, but it looks like only the last object ("Test Ten") is kept in loaded_contacts. Search for "Test Ten" will retrieve its value from the dictionary, but search for other names like "Test Nine" will return a key error. Sorry I know the original question about the "None" object is already answered (many thanks for that!). I'm just still trying to make the whole thing work... – jojo Apr 19 '22 at 01:13
  • @jojo Assigning `loaded_contacts = pickle.load(f)` overwrites whatever previous value was in `loaded_contacts`, so of course it ends up with only the last object. Did you intend to save each loaded item in a list? – John Gordon Apr 19 '22 at 01:37
  • Yes, I'd need to save each loaded item in a 'dictionary' if possible. I'm still trying to make it work :) – jojo Apr 19 '22 at 01:53
  • I've solved this with `loaded_contacts.update(pickle.load(f))` in the loop, so all the objects from file `f` would be stored in the dictionary `loaded_contacts` :) – jojo Apr 19 '22 at 02:49