Albi
Albi

Reputation: 33

Sorting CSV into a dictionary in a Class

I have been trying to create a Class that will accept a CSV file, read it and sort it into a dictionary using one of the rows as a key. Everything i can find online in relation to csv files, deals with them outside of classes.

I am trying to Open the csv file (called books.csv) and read each row into a private attribute of the Shelf class called __books that is a dictionary. The dictionary should use the ISBN as a key.

I want to get the csv file books.csv into the dictionary __books{ } and put the 4th row as a key but every time I run it, it states that the books.csv file isn't being read into the dictionary.

The first piece of code is:

class Book:

    def __init__(self, title, author, price, isbn):
        self.title = title
        self.author = author
        self.price = price
        self.isbn=isbn

    def getISBN(self):
         return self.isbn


class Shelf:

    __books={}

    def __init__(self,filename):
        f=open(filename, encoding="utf8")
        csvreader = csv.reader(f)

    for row in csvreader:
        abook=Book(row[0],row[1],row[2],row[3])
        self.__books[row[4]]=abook

This code will be called into another file and the corresponding code is:

aShelf=Shelf("books.csv")
    abook=aShelf._Shelf__books["0743482836"]
    if abook.author == "A. Goose":
        pass
    else:
        raise Exception

Upvotes: 0

Views: 162

Answers (2)

Roman Kutlak
Roman Kutlak

Reputation: 2784

There are a few issues with the code. Firstly, I think the __books should be defined as an instance variable, not a class variable. The way you have it, if you create multiple instances of Shelf they will share the dictionary (I don't think that is the intent).

Secondly, when you are reading files, you should use the with statement so that you don't leak resources. Thirdly, as some of the comments suggest, you should pass the number of the column that you want to use as a key as an argument.

And lastly, you could provide __getitem__ to allow easier syntax when looking up books.

class Shelf:
    def __init__(self,filename, key_no=3):
        self._books={}
        with open(filename, encoding="utf8") as f:
            csvreader = csv.reader(f)
            for row in csvreader:
                abook = Book(*[x.strip() for x in row])
                self._books[row[key_no].strip()] = abook

    def __getitem__(self, isbn):
        return self._books[isbn]


aShelf=Shelf("books.csv")
print(aShelf._books)

abook = aShelf["0743482836"]
if abook.author == "A. Goose":
    pass
else:
    raise Exception

Upvotes: 1

Paul Rooney
Paul Rooney

Reputation: 21619

I believe your error is that the section that inserts into the dictionary is not indented correctly and so doesn't run when your __init__ method is called

Before

def __init__(self,filename):
    f=open(filename, encoding="utf8")
    csvreader = csv.reader(f)

for row in csvreader:
    abook=Book(row[0],row[1],row[2],row[3])
    self.__books[row[4]]=abook

After

def __init__(self,filename):
    f=open(filename, encoding="utf8")
    csvreader = csv.reader(f)

    ''' indented here '''
    for row in csvreader:
        abook=Book(row[0],row[1],row[2],row[3])
        self.__books[row[4]]=abook

You can also eliminate the ugly index syntax in your loop by unpacking the values as a tuple.

NOTE: This section is based on the OPs updated information in comments, relating to which indexes in the csv data represent which book parameters. The earlier sections are not updated.

for _, title, author, price, isbn, *_ in csvreader:
    self.__books[isbn] = Book(title, author, price, isbn)

That should be much more readable. The only thing that isnt clear is which index is the isbn, but I'll let you figure that one out.

Upvotes: 0

Related Questions