pythonpython-3.xfile-io

Python doesn't write back to file modified line


I have this function that is supposed to modify an int in the format of a string and write back the changes to the file. I am sure the isbn passed to the function is correct.

Here is my function:

def decrementBookUnits(isbn):
    with open('books.txt', 'r') as booksFile:
        content = booksFile.readlines()
        for line in content:
            if str(isbn) in line:
                wordList = line.split(' -- ')
                wordList2 = wordList[6].split(':')
                if int(wordList2[1]) == isbn:
                    wordList3 = wordList[5].split(':')
                    wordList[1] = wordList3[1].replace('3', '666')
                    with open('books.txt', 'w') as booksFile:
                        booksFile.writelines(content)

And here is what the books.txt file looks like for context:

    ID:0 -- Title:ff -- Author:dfdf -- Editor:df -- YearOfRelease:2000 -- NumberOfUnits:3 -- ISBN:123
    ID:01 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2003 -- NumberOfUnits:1 -- ISBN:1234
    ID:03 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2000 -- NumberOfUnits:0 -- ISBN:321
    ID:04 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2000 -- NumberOfUnits:4 -- ISBN:44
    ID:05 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:20055 -- NumberOfUnits:3 -- ISBN:33
    ID:06 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2006 -- NumberOfUnits:6 -- ISBN:70
    ID:07 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2011 -- NumberOfUnits:7 -- ISBN:54
    ID:08 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2000 -- NumberOfUnits:4 -- ISBN:44
    ID:09 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:20055 -- NumberOfUnits:3 -- ISBN:33
    ID:10 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2006 -- NumberOfUnits:6 -- ISBN:70
    ID:11 -- Title:title -- Author:author -- Editor:editor -- YearOfRelease:2011 -- NumberOfUnits:7 -- ISBN:54
    ID:12 -- Title:hhh -- Author:hhh -- Editor:hh -- YearOfRelease:2004 -- NumberOfUnits:2 -- ISBN:77

For example, the passed value of isbn is 33 and the function is supposed to change the NumberOfUnits from 3 to 666 but it doesn't. Thanks in advance.


Solution

  • As mentioned in the comments there a couple of bigger issues with your code. First, the writing to a file that is already open; an issue that can be easily corrected with unindenting code. Second, the bigger issue is your assumption around how variables work. This second issue is more difficult to solve.

    Here's a rewrite of your code, breaking up the functions into three operations:

    1. A function for opening the file and turning the contents into a list of dictionaries object. This will make working with this data much easier for any other functionality you want to build in.

    2. A function specifically to decrement a specific ISBN in this new books object. Separating this out to a single function helps compartmentalize the code and allows you to decrement more ISBN's while you have the books file open inside this object if you wish. This function is still spitting out 666 but that could be changed to books[idx]['ISBN'] = str(int(books[idx]['ISBN']) - 1) or what-have-you

    3. A function to write the new books object out to a file in the same format from which it was read.


    def getBooks(file):
        """
        The function `getBooks` reads a file, splits its content into lines, and then splits each line into
        key-value pairs to create a list of dictionaries representing books.
        
        :param file: The `file` parameter is the name or path of the file that contains the book information
        :return: a list of dictionaries, where each dictionary represents a book with key-value pairs for
        its attributes.
        """
        #open the file
        with open(file, 'r') as booksFile:
            content = booksFile.read().splitlines()
        
        #split each line to a list
        lines=[line.split(' -- ') for line in content]
        
        #turn entries in each line into a key:val pair for a dictionary
        books=[dict(entry.split(':') for entry in line) for line in lines]
        
        return books
    
    def decrementBookUnits(books,isbn):   
        """
        The function `decrementBookUnits` takes a list of books and an ISBN number, and updates the ISBN of
        the book with the given ISBN number to '666'.
        
        :param books: A list of dictionaries representing books. Each dictionary contains information about
        a book, including its ISBN
        :param isbn: The `isbn` parameter is the ISBN (International Standard Book Number) of the book that
        you want to decrement the units for
        :return: the updated list of books after decrementing the units of a book with the given ISBN.
        """
        #iterate through the books list 
        for idx, entry in enumerate(books):
            #check if this entry has our isbn
            if entry['ISBN'] == str(isbn):
                #update the books entry's ISBN by updating the 
                # actual `books` object directly using the idx
                books[idx]['ISBN'] = '666'  
    
                #or actually decrement:
                #books[idx]['ISBN'] = str(int(books[idx]['ISBN']) - 1)
        return books
    
    def writeBooks(books, file):
        """
        The function `writeBooks` takes a list of books and a file name as input, opens the file, iterates
        through the books, and writes each book entry to the file in a specific format.
        
        :param books: The "books" parameter is a list of dictionaries. Each dictionary represents a book and
        contains key-value pairs for different attributes of the book, such as title, author, genre, etc
        :param file: The `file` parameter is the name or path of the file where the books will be written
        """
        #Open file, iterate entries, and write back out
        with open(file, 'w') as booksFile:
            for entry in books:
                booksFile.writelines(' -- '.join(['='.join([k,v]) for k,v in entry.items()]) + '\n')
    
    #get the contents of the book an object
    # defined by the function
    books=getBooks('books.txt')
    
    #see what this looks like
    print(books) 
    
    #decrement books with an ISBN of 33
    books=decrementBookUnits(books,33)
    
    #write our books object back out to a file
    # using a new file so this can be run over 
    # and over again.
    writeBooks(books,'books2.txt')