I'm learning python and I have written a bank program incorporating all the skills I've gathered so far. Using the program, one can create user profiles (name, password, account balance) which will be stored in three different text files. One can then do the normal bank transactions: deposit, withdraw, check balance or even close and delete their entire bank profile.Every transaction is accompanied by a password check.(There are two python files: Bank.py, filestore.py and three text files: cusbalfile.txt, cusnamefile.txt, cuspassfile.txt)Bank.pyimport filestoreimport timeimport datetime##############################################################################This is the function that is called at the beginning of the programdef postbank():                                                                         ###    print (Welcome to PostBank, We care for you\n)                                    ###    prompt=int(raw_input(To open a new bank account, Press 1\n+                   ###                                             To access your existing account & transact press 2\n))    ###    if prompt==1:                                                                       ###        cus=BankAccount()#creates a new customer profile                                ###    elif prompt==2:                                                                     ###        cus=ReturnCustomer()#checks for existing customer                               ###    else:                                                                               ###        print You have pressed the wrong key, please try again                        ###        postbank()                                                                      ################################################################################################class for creating an instance of a new back account and other default bank functionsclass BankAccount:    Class for a bank account    type=Normal Account    def __init__(self):        ##calls functions in the module filestore        self.username, self.userpassword, self.balance=filestore.cusaccountcheck()        print (Thank you %s, your account is set up and ready to use,\n a 100 pounds has been credited to your account %self.username)        time.sleep(2)        self.userfunctions()    def userfunctions(self):        print(\n\nTo access any function below, enter the corresponding key)        print (To:check Balance, press B.deposit cash,  press D.withdraw cash, press W.Delete account press X.exit service,  press E\n:),        ans=raw_input().lower()        if ans=='b':            ##passcheck function confirms stored password with user input            self.passcheck()            self.checkbalance()        elif ans=='d':            self.passcheck()            self.depositcash()        elif ans=='w':            self.passcheck()            self.withdrawcash()        elif ans=='x':            print (%s, your account is being deleted%self.username)            time.sleep(1)            print (Minions at work)            time.sleep(1)            filestore.deleteaccount(self.username)            print (Your account has been successfuly deleted, goodbye)        elif ans=='e':            print (Thank you for using PostBank Services)            time.sleep(1)            print (Goodbye %s %self.username)            exit()        else:            print No function assigned to this key, please try again            self.userfunctions()    def checkbalance(self):        date=datetime.date.today()        date=date.strftime('%d-%B-%Y')        self.working()        print (Your account balance as at {} is {}).format(date, self.balance)        self.transact_again()    def withdrawcash(self):        amount=float(raw_input(::\n Please enter amount to withdraw\n: ))        self.balance-=amount        self.working()        print (Your new account balance is %.2f %self.balance)        print (::\n)        filestore.balupdate(self.username, -amount)        self.transact_again()    def depositcash(self):        amount=float(raw_input(::\nPlease enter amount to be deposited\n: ))        self.balance+=amount        self.working()        print (Your new account balance is %.2f %self.balance)        print (::\n)        filestore.balupdate(self.username, amount)        self.transact_again()    def transact_again(self):        ans=raw_input(Do you want to do any other transaction? (y/n)\n).lower()        self.working()        if ans=='y':            self.userfunctions()        elif ans=='n':            print (Thank you for using PostBank we value you. Have a good day)            time.sleep(1)            print (Goodbye {}).format(self.username)            exit()        elif ans!='y' and ans!='n':            print Unknown key pressed, please choose either 'N' or 'Y'            self.transact_again()    def working(self):        print(working),        time.sleep(1)        print (..)        time.sleep(1)        print(..)        time.sleep(1)    def passcheck(self):        prompts user for password with every transaction and counterchecks it with stored passwords        b=3        while b>0:            ans=raw_input(Please type in your password to continue with the transaction\n: )            if ans==self.userpassword:                return True            else:                print That is the wrong password                b-=1                print (%d more attempt(s) remaining %b)        print (Account has been freezed due to three wrong password attempts,\n contact your bank for help, bye bye)        time.sleep(1)        print (...)        time.sleep(1)        print(...)        time.sleep(1)        exit()class ReturnCustomer(BankAccount):    type=Normal Account    def __init__(self):        self.username, self.userpassword, self.balance=filestore.oldcuscheck()        self.userfunctions()postbank() ##calling the function to run the programfilestore.py##creating empty lists everytime the program is initializedcusnames=[]cuspasswords=[]cusbalance=[]##opening the storage files to collect old customer datanamefile=open(cusnamefile.txt, r)passfile=open(cuspassfile.txt, r)balfile=open(cusbalfile.txt, r)##populate the empty lists with data from storage files##check list of customer namesfor line in namefile:        cusnames.append(line[:-1])namefile.close()##check list of customer passwordsfor line in passfile:        cuspasswords.append(line[:-1])passfile.close()##check list of customer balancesfor line in balfile:        cusbalance.append(line[:-1])balfile.close()##function creates a new user def cusaccountcheck():        name=        pin=        while name not in cusnames and len(name)<3:                name=raw_input(Please type in your name for this new bank account\n)                if name not in cusnames:                        cusnames.append(name)                        filewrite(cusnames)                        break                print(Sorry, that user name is already in use)                ans=raw_input(Are you already a member at this bank? (y/n)\n)                if ans.lower()=='y':                        oldcuscheck()                else:                        cusaccountcheck()        while len(pin)<4:                pin=raw_input(Please assign a password to this account, pin should be at least 5 characters\n)                if len(pin)>4:                        print your pin has been successfully saved                        print Remember to always keep your pin safe and don't disclose it to anybody                        cuspasswords.append(pin)                        cusbalance.append(0)                        balance=100.0                        cusbalance[cusnames.index(name)]=balance                        filewrite(cuspasswords)                        filewrite(cusbalance)                        break                print (Sorry, that is a short password)        return name,pin, balance##Function to check returning customerdef oldcuscheck():        name=        while name not in cusnames:                name=raw_input(What is your name?\n)                if name in cusnames:                        username=name                        userpassword=cuspasswords[cusnames.index(name)]                        balance=float(cusbalance[cusnames.index(name)])                        return username, userpassword, balance                else:                        print (Sorry %s, It looks like you didn't spell you name correctly or your name is not in our records%name)                        again=raw_input(would like to type in your name again? (y/n))                        if again.lower()=='y':                                oldcuscheck()                        else:                                print (Bye bye, thank you for trying Postbank)                                exit()##This function writes new data into the storage files whenever called upon.      def filewrite(item):        if item==cusnames:                text=open(cusnamefile.txt,w)                for i in item:                        text.write(i+\n)                text.close()        elif item==cuspasswords:                text=open(cuspassfile.txt, w)                for i in item:                        text.write(i+\n)                text.close()        elif item==cusbalance:                text=open(cusbalfile.txt, w)                for i in item:                        text.write(str(i)+\n)                text.close()###This function updates the account balance after a withdraw or deposit transactiondef balupdate(ind, amount):        accountnumber=cusnames.index(ind)        accountbal=float(cusbalance[accountnumber])        accountbal+=amount        cusbalance[accountnumber]=accountbal        text=open(cusbalfile.txt, w)        for i in cusbalance:                text.write(str(i)+\n)        text.close()###This function deletes an existing account and any data that was stored about it is cleareddef deleteaccount(name):        accountnumber=cusnames.index(name)        del cusnames[accountnumber]        filewrite(cusnames)        del cusbalance[accountnumber]        filewrite(cusbalance)        del cuspasswords[accountnumber]        filewrite(cuspasswords)        return Nonecusbalfile.txt450693.06782449.03525.06000000.05532.0cusnamefile.txtLoise NjoguMoses NjagiMakerFuse LimitedDadDennis Meyercuspassfile.txt1234509876makerpasswordthedadpassword234234
Bank accounts - names, balances and passwords
python;beginner;python 2.7
Separation of concernsAlso known as the single responsibility principle.Perhaps the biggest issue I see in this post is that the account management logic (deposit, withdraw, others) is mixed with handling keyboard input and printing output.Take this function for example:def withdrawcash(self):    amount=float(raw_input(::\n Please enter amount to withdraw\n: ))    self.balance-=amount    self.working()    print (Your new account balance is %.2f %self.balance)    print (::\n)    filestore.balupdate(self.username, -amount)    self.transact_again()Too many things are happening here:Reading from keyboardUpdating balance in memoryUpdating balance in a file, via reference to external object filestorePrintingRecursive call to user interface loopBut actually it's a bit late to separate concerns here.They should have been separated at the class design level.These actions don't belong in a BankAccount class.Consider this alternative organization of classes:Bank: in charge of bank accounts in the bank, and storageBankAccount: in charge of managing the balance of an accountHas a reference to BankBankApp: in charge of the interactive loop, reading input, printing output, and callingHas a reference to Bank and AccountThe interactive BankApp could look more like this:class BankApp:    # ...    def main(self):        while True:            if action == ACTION_QUIT:                break            if action == ACTION_WITHDRAW:                self.handle_withdraw()            # ...    def handle_withdraw(self):        # print welcome        # input amount        if self.bank.withdraw(amount):            # print success        else:            # print failureKey points about the handle_withdraw method:Doesn't manipulate the balance directly, it delegates that task to self.bank.withdrawDoesn't need to know where the balance is stored. That's a job for self.bank.withdraw. It could be in memory, in file, on the moon, or all of these. It's not the responsibility of the interactive app to know such implementation detailsDoesn't control the user interface loop: it handles a one-time withdraw action and returns. Awareness of the user interface loop (of repeated transactions with the bank) is not necessary at this levelConsider that the withdrawal might not succeed. My bank certainly won't cash out one million bucks for meThis example is incomplete, but I hope it helps you get started.If you can get your hands on the book Code Complete,chapter 6 about class design and Abstract Data Types will give you great insight,and naturally drive you towards better separation of concerns and more flexible design.Working with filesThis way of working with files is error prone and not recommended:namefile=open(cusnamefile.txt, r)# ...for line in namefile:        cusnames.append(line[:-1])namefile.close()The recommended technique:with open(cusnamefile.txt, r) as namefile:    for line in namefile:        cusnames.append(line[:-1])Notice that I didn't close namefile.I didn't need to, the with block takes care of that.By the way, since reading is the default mode, you can drop that r.Using list comprehensionsInstead of:cusnames=[]with open(cusnamefile.txt) as namefile:    for line in namefile:        cusnames.append(line[:-1])List comprehensions are short and sweet and powerful:with open(cusnamefile.txt) as namefile:    cusnames = [line[:-1] for line in namefile]Self-inflicted pain?What's up with the trailing ### on these lines?#This is the function that is called at the beginning of the programdef postbank():                                                                         ###    print (Welcome to PostBank, We care for you\n)                                    ###    prompt=int(raw_input(To open a new bank account, Press 1\n+                   ###                                             To access your existing account & transact press 2\n))    ###    if prompt==1:                                                                       ###        cus=BankAccount()#creates a new customer profile                                ###    elif prompt==2:                                                                     ###        cus=ReturnCustomer()#checks for existing customer                               ###    else:                                                                               ###        print You have pressed the wrong key, please try again                        ###        postbank()                                                                      ##############################################################################################Although they are nicely aligned,consider what will happen when you will need to change the code of these lines: that's right, you may need to add and remove spaces to keep the ### aligned. Whose value is highly questionable.Probably these cause you more pain than benefit,I suggest to delete these.In general, any unnecessary stuff hurts you more in the long run that it helps.    Where is this PostBank anyway?class BankAccount:    def __init__(self):        # ...        print (Thank you {username}, your account is set up and ready to use,.format(username=self.username)            + \n + a 100 pounds has been credited to your account)I'd like to open a few dozen accounts and then withdraw 100 pounds from each ;-)