1

I have a small program to track monthly balances. This worked fine as is, and then I added in the section to write to a .txt file at the bottom. I did some searching but can't figure out a way to make it work. Basically I want to keep appending to this test.txt file. Every time I enter a new month/account/balance I want that appended to the file.

The alternative is to append to the test.txt file after 'exit', so it doesn't do it every loop. Not sure which way is more efficient

***EDIT****

This updated code now creates test.txt file but the file is blank after each loop

Secondary question - I have heard this would be better off using a Class for this, but I haven't any idea how that would look. If someone wants to demonstrate that would be awesome. This isn't homework, this is strictly learning on my own time.

Any ideas? Thanks

# create program to track monthly account balances

savings = []

def add_accounts(date, account, balance):

    savings.append({'date': date, 'account': account, 'balance': 
balance})

def print_accounts():
    print(savings)

while True:

    date = input('Enter the date, type exit to exit program: ')
    if date == 'exit':
        break
    account = input('Enter the account: ')
    balance = int(input('Enter the balance: '))

    add_accounts(date, account, balance)
    print_accounts()
with open('test.txt', 'a') as f:
for row in savings():
    print (row)
    f.write(str(savings[-1]))
    file.close()
JD2775
  • 3,658
  • 7
  • 30
  • 52
  • 1
    `"w"` overwrites the file - see https://stackoverflow.com/questions/4706499/how-do-you-append-to-a-file – Izaak van Dongen Sep 02 '17 at 22:29
  • @IzaakvanDongen . ah yes, thanks, should be 'a'. that doesnt solve the issue though. Still throwing a: for row in print_accounts(): TypeError: 'NoneType' object is not iterable . error – JD2775 Sep 02 '17 at 22:31
  • can you please copy the traceback of the error? – Kshitij Mittal Sep 02 '17 at 22:32
  • 1
    because the method `print_accounts` isnt returning anything. It is just printing. (explaining the error) – Sachin Sep 02 '17 at 22:33
  • 1
    If you're updating it for each new addition you don't need to iterate over everything in `savings` (aside from that, `print_accounts` doesn't return anything). Just do something like `f.write(str(savings[-1]))` – Izaak van Dongen Sep 02 '17 at 22:33
  • @KshitijMittal added to OP. thanks – JD2775 Sep 02 '17 at 22:34
  • 1
    As Sachin and Izaak suggested, print_accounts() function is not returning anything. You can iterate over savings array. – Kshitij Mittal Sep 02 '17 at 22:36
  • @KshitijMittal. Thanks, I updated my code (see above). It works now, but is returning a blank text file... – JD2775 Sep 02 '17 at 22:42

2 Answers2

2

The problem with your original code is that print_accounts() doesn't return anything, yet you attempt to perform operations on its (nonexistent) return value.

Here is a version of your program made using classes, and with a few corrections:

class Account:
    def __init__(self, id, date, balance):
        self.id = id
        self.date = date
        self.balance = balance

    def getString(self):
        return self.id + "," + self.date + "," + str(self.balance)

savings = []

def add_account(date, account, balance):
    savings.append(Account(date, account, balance))

def print_accounts():
    for account in savings:
        print(account.getString())

while True:
    date = input("Enter the date, type exit to exit program: ")
    if date.lower() == "exit":
        break
    else:
        account = input('Enter the account: ')
        balance = int(input('Enter the balance: '))
        add_account(date, account, balance)
        print_accounts()
        with open("test.txt", "w") as file:
            for account in savings:
                file.write(account.getString() + "\n")

Some explanation regarding the class: The Account class has 3 fields: id, date, and balance. These fields are defined in the constructor (__init__()). The class also has a method, getString(), which I use to get the string representation of each instance.

Over all, the following changes have been made:

  • Create an Account class, which serves as the template for the object which holds the data of each account.
  • Use a loop to print accounts and write them to the file.
  • Turn date into lowercase before checking to see if it is equal to "exit". This is a minor change but a good habit to have.
  • Removed f.close(), as it is unnecessary when using a with open() statement.
  • Created a custom string representation of each instance of Account, consistent with what you would otherwise get.

That last one is achieved via defining the getString method in the account class. There is nothing special about it, it is merely what we use to get the string representation.

A better but quite more advanced way to achieve that is by overriding the __str__ and __repr__ methods of the base object. These are essentially hidden functions that every class has, but which python defines for us. The purpose of these two specific ones is to give string representations of objects. The default code for them doesn't produce anything meaningful:

<__main__.Account object at 0x0000000003D79A58>

However, by overriding them, we can use str() on instances of Account, and we will get a string representation in the exact format we want. The modified class will look like so:

class Account:
    def __init__(self, id, date, balance):
        self.id = id
        self.date = date
        self.balance = balance

    def __repr__(self):
        return self.id + "," + self.date + "," + str(self.balance)
    def __str__(self):
        return self.__repr__()

This also eliminates the need to loop through savings when writing to the file:

with open("test.txt", "w") as file:
    for account in savings:
        file.write(account.getString() + "\n")

Turns into:

with open("test.txt", "w") as file:
    file.write(str(savings))

This wouldn't have worked before, as str() would have given us the gibberish data you saw earlier. However, now that we have overridden the methods, it works just fine.

stelioslogothetis
  • 9,371
  • 3
  • 28
  • 53
  • Thank you very much for taking the time to write this out and explain this to me. Great reference for me – JD2775 Sep 02 '17 at 23:36
1

Try this code (use -1 to exit):

savings = []

def add_accounts(date, account, balance):

    savings.append({'date': date, 'account': account, 'balance': 
balance})

def print_accounts():
    print(savings)

while True:

    date = input('Enter the date, type exit to exit program: ')
    if date == -1:
        break
    account = input('Enter the account: ')
    balance = int(input('Enter the balance: '))

    add_accounts(date, account, balance)
    print_accounts()
with open('test.txt', 'a') as f:
    for row in savings:
        print (row)
        f.write(str(savings[-1]))
        f.close()
Kshitij Mittal
  • 2,698
  • 3
  • 25
  • 40
  • @jd2775 Does this work? are there still any issues remaining? – Kshitij Mittal Sep 02 '17 at 22:58
  • can you do me a favor and run the code you showed me here when you get time? I still get a blank output text file when running. Just noticing it now. Curious if you get the same result. Thanks – – JD2775 Sep 03 '17 at 05:12
  • I ran the code yesterday itself, seems to be working fine on my machine. Are u getting an empty file on running the exact code? – Kshitij Mittal Sep 03 '17 at 05:20