pickle.load() adds a None object at the end - python

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

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)

Related

How to fix the errors in my code for making a dictionary from a file

This is what I am supposed to do in my assignment:
This function is used to create a bank dictionary. The given argument
is the filename to load. Every line in the file will look like key:
value Key is a user's name and value is an amount to update the user's
bank account with. The value should be a number, however, it is
possible that there is no value or that the value is an invalid
number.
What you will do:
Try to make a dictionary from the contents of the file.
If the key doesn't exist, create a new key:value pair.
If the key does exist, increment its value with the amount.
You should also handle cases when the value is invalid. If so, ignore that line and don't update the dictionary.
Finally, return the dictionary.
Note: All of the users in the bank file are in the user account file.
Example of the contents of 'filename' file:
Brandon: 115.5
James: 128.87
Sarah: 827.43
Patrick:'18.9
This is my code:
bank = {}
with open(filename) as f:
for line in f:
line1 = line
list1 = line1.split(": ")
if (len(list1) == 2):
key = list1[0]
value = list1[1]
is_valid = value.isnumeric()
if is_valid == True
value1 = float(value)
bank[(key)] = value1
return bank
My code returns a NoneType object which causes an error but I don't know where the code is wrong. Also, there are many other errors. How can I improve/fix the code?
Try this code and let me explain everything on it because it depends on how much you're understanding Python Data structure:
Code Syntax
adict = {}
with open("text_data.txt") as data:
"""
adict (dict): is a dictionary variable which stores the data from the iteration
process that's happening when we're separating the file syntax into 'keys' and 'values'.
We're doing that by iterate the file lines from the file and looping into them.
The `line` is each line from the func `readlines()`. Now the magic happens here,
you're playing with the line using slicing process which helps you to choose
the location of the character and play start from it. BUT,
you'll face a problem with how will you avoid the '\n' that appears at the end of each line.
you can use func `strip` to remove this character from the end of the file.
"""
adict = {line[:line.index(':')]: line[line.index(':')+1: ].strip('\n') for line in data.readlines()}
print(adict)
Output
{' Brandon': '115.5', ' James': '128.87', ' Sarah': '827.43', ' Patrick': "'18.9"}
In term of Value Validation by little of search you will find that you can check the value if its a number or not
According to Detect whether a Python string is a number or a letter
a = 5
def is_number(a):
try:
float (a)
except ValueError:
return False
else:
return True
By Calling the function
print(is_number(a))
print(is_number(1.4))
print(is_number('hello'))
OUTPUT
True
True
False
Now, let's back to our code to edit;
All you need to do is to add condition to this dict..
adict = {line[:line.index(':')]: line[line.index(':')+1: ].strip(' \n') for line in data.readlines() if is_number(line[line.index(':')+1: ].strip('\n')) == True}
OUTPUT
{'Brandon': '115.5', 'James': '128.87', 'Sarah': '827.43'}
You can check the value of the dict by passing it to the function that we created
Code Syntax
print(is_number(adict['Brandon']))
OUTPUT
True
You can add more extensions to the is_number() function if you want.
You're likely hitting the return in the else statement, which doesn't return anything (hence None). So as soon as there is one line in your file that does not contain 2 white-space separated values, you're returning nothing.
Also note that your code is only trying to assign a value to a key in a dictionary. It is not adding a value to an existing key if it already exists, as per the documentation.
This should effectively do the job:
bank = {}
with open(filename) as file:
for line in file:
key, val = line.rsplit(": ", 1) # This will split on the last ': ' avoiding ambiguity of semi-colons in the middle
# Using a trial and error method to convert number to float
try:
bank[key] = float(val)
except ValueError as e:
print(e)
return bank

How do I use data from one function in another function in python?

Hello I am pretty new to python and I want to do the following:
I have a function that opens a file, reads the file, closes the file and returns the data:
def getFastaFromFile(filename):
""" Read a fasta file (filename) from disk and return
its full contents as a string"""
inf=open(filename)
data=inf.read()
inf.close()
return data
The data that is being returned are a few lines with strings.
What I want to do is have another function that uses the data from the first function and perform the .readlines(), .readline() and .count() commands
My second function:
def printTableFromFasta(fastarec):
a= data.readlines()
for i in range(a)
b= data.readline()
c= b.count('A')
print(c)
As output I would like to print the amount of times string "A" appears for every line from the data. The problem I get with this code is that the data doesn't get recognized.
First, you need to pass the data you are wanting to read into the second function, like so
def printTableFromFasta(data):
In order to get this from your first function, try returning the entire contents of the file
def getFastaFromFile(filename):
with open(filename, 'r') as inf: # handles open and close
data = inf.readlines() # Returns the entire file as a list of strings
return data
Your function call will look something like this
printTableFromFasta(getFastaFromFile(filename))
Then, in your second function, you don't need to call readlines, it's already a list.
def printTableFromFasta(data):
for line in data # look at each line
print(line.count('A')) # count 'A'
Edit:
To only read from the second function and not touch the first function
def printTableFromFasta(filename):
with open(filename, 'r') as inf: # handles open and close
for line in inf.readlines() # look at each line
print(line.count('A')) # count 'A'
Remember that the data variable in the first function is local. It cannot be accessed from outside the function it is defined in.
For example, the getName() function returns a variable which is locally called data but you access the value by calling the function.
def getName(user_id):
data = "Your name is " + str(user_id)
return data
# Throws an error, because data in undefined
name = getName("Bobby")
print(data)
# Working code, prints "Your name is Bobby"
name = getName("Bobby")
print(name)
There are no rules against calling one function from inside another.
Instead of a = data.readlines() try a = getFastaFromFile("dna.fasta') as well as changing data = inf.read() to data = inf.readlines()

Don't understand the cause of this AttributeError

What's wrong with that code? When I run it tells me this:
Traceback (most recent call last):
line 24, in <module>
people.append(Dict)
AttributeError: 'str' object has no attribute 'append'
My code:
live = 1
while live == 1:
#reading Database
dataRead = open ("db.txt","r")
if dataRead.read() != " ":
dataRead.close()
people = open ('db.txt','r').read()
do = input ('What Do You Want ? (Search , add) :\n')
#add people
if do == 'add':
#Get The New Data
n_Name = input ('enter the new name:\n')
n_age = input ('enter the new age:\n')
#new Dict
Dict = {'Name:':n_Name,'age':n_age}
people.append(Dict)
#adding people to file
dataWrite = open ("db.txt","w")
dataWrite.write(str(people))
dataWrite.close()
live = 0
The problem is, on line 24, you try to append a dictionary to a string. When you read the db file, it read it as a string. Also the code is really messy and there are a lot better ways to do it. But that's besides the point, the append() method is for lists and the variable "people" is a string, according to your error output.
It says that people is str then it doesn't have an append method. You should just concatenate strings to get them together.
Do:
people += '<append string>'
Have in mind you are trying to append a dictionary to a string. This will throw TypeError cause those type of elements can't be concatenated that way. You should do first: str(dict) to concatenate them.
You're also using a reserved word like dict as a variable. Change it to my_dict or other allowed name.

Removing duplicates from an attribute of a class variable

I'm extremely new to python and was having some trouble with removing duplicate values from an attribute of a class (I think this is the correct terminology).
Specifically I want to remove every value that is the same year. I should note that I'm printing only the first four value and searching for the first four values. The data within the attribute is actually in Yearmonthday format (example: 19070101 is the year 1907 on the first on january).
Anyways, here is my code:
import csv
import os
class Datatype:
'Data from the weather station'
def __init__ (self, inputline):
[ self.DATE,
self.PRCP] = inputline.split(',')
filename ='LAWe.txt'
LAWd = open(filename, 'r')
LAWefile = LAWd.read()
LAWd.close()
'Recognize the line endings for MS-DOS, UNIX, and Mac and apply the .split() method to the string wholeFile'
if '\r\n' in LAWefile:
filedat = LAWefile.split('\r\n') # the split method, applied to a string, produces a list
elif '\r' in LAWefile:
filedat = LAWefile.split('\r')
else:
filedat = LAWefile.split('\n')
collection = dict()
date= dict()
for thisline in filedat:
thispcp = Datatype(thisline) # here is where the Datatype object is created (running the __init__ function)
collection[thispcp.DATE] = thispcp # the dictionary will be keyed by the ID attribute
for thisID in collection.keys():
studyPRP = collection[thisID]
if studyPRP.DATE.isdigit():
list(studyPRP.DATE)
if len(date[studyPRP.DATE][0:4]):
pass #if year is seen once, then skip and go to next value in attribute
else:
print studyPRP.DATE[0:4] #print value in this case the year)
date[studyPRP.DATE]=studyPRP.DATE[0:4]
I get a this error:
Traceback (most recent call last):
File "project.py", line 61, in
if len(date[studyPRP.DATE][0:4]):
KeyError: '19770509'
A key error (which means a value isn't in a list? but it is for my data) can be fixed by using a set function (or so I've read), but I have 30,000 pieces of information I'm dealing with and it seems like you have to manually type in that info so that's not an option for me.
Any help at all would be appreciated
Sorry if this is confusing or nonsensical as I'm extremely new to python.
Replace this
if len(date[studyPRP.DATE][0:4])
by this
if len(date[studyPRP.DATE[0:4]]):
Explanation :
In the first line you are selecting the whole date as the key KeyError: '19770509' in the 4 first entry of date
In the correction you send the the first 4 character of the date(the year) in the dictionary
Don't know what exactly you want here. I'll reply based on I can help you on what.
Your error is because you are accessing your year in data before you are adding it.
Also, what you are adding to your collection is like
{
<object>.DATE: <object>
}
I don't know what you need here. Your lower for loop can be written as under:
for thisID in collection:
if thisID.isdigit():
if thisID[0:4] in date and len(date[thisID[0:4]]):
#if year is seen once, then skip and go to next
# value in attribute
pass
else:
print thisID[0:4] #print value in this case the year)
date[thisID[0:4]]=thisID[0:4]
Note your studyPRP.DATE is same as thisID.

Merging list objects in python

I have a pickle file having many objects. I need to have one proper object by combining all the other objects in the file. How can I do that. I tried using many commands, but none seems to work.
objs = []
while True:
try:
f = open(picklename,"rb")
objs.append(pickle.load(f))
f.close()
except EOFError:
break
Like the one above as shown.
OBJECT stored image :
<nltk.classify.naivebayes.NaiveBayesClassifier object at 0x7fb172819198>
<nltk.classify.naivebayes.NaiveBayesClassifier object at 0x7fb1719ce4a8>
<nltk.classify.naivebayes.NaiveBayesClassifier object at 0x7fb1723caeb8>
<nltk.classify.naivebayes.NaiveBayesClassifier object at 0x7fb172113588>
You should use .extend() to append all items in the list to objs:
(Assuming pickle.load(f) returns a list of objects)
objs.extend(pickle.load(f))

Categories