0

I completed an assignment for a scripting class I'm taking, and it worked fine, but I wanted to take it a step further, which has sent me down a rabbit hole. The script is for an ATM transaction.

I added a while loop to give the user additional options, instead of just ending the program. But I found the my account_balance variable remained equal to the default value set initially (in this case 500.25). I've been trying to find ways to make this update as the user goes through the program, in theory it should go as follows:

  1. User checks balance = $500.25 --------------> balance(account_balance)
  2. User makes deposit = $500.00 ---------------> deposit(account_balance, amount)
  3. User checks balance again = $1,000.25 ---> program always returns with $500.25

To try and make this work, I made another function transactionGo(pin), and set all the other functions inside it, hoping this would update the variable account_balance, that didn't work either, and in fact caused more problems, I feel I'm going down a road I shouldn't.

import sys

# -------------------------- DECLARE variables for balance, deposit, and withdrawal --------------------------

account_balance = float(500.25)                                                 # Starting balance indicated by Codio
#balance = 100
amount = 0
#deposit_amount = 0                                                              # Declare variable 'deposit_amount'
#withdrawal_amount = 0                                                           # Declare variable 'withdrawal_amount'
pin = ''

# -------------------------- DEFINE FUNCTIONS - balance, withdrawal, and deposit -----------------------------
def transactionGo (pin):
  pin = int(input("Please enter your 5 digit pin number:\n"))
  
  def balance(account_balance):                                                   # Define balance function
    print("Your current balance is $%.2f" % (account_balance))                    # Prints the current available balance

  def deposit(account_balance, amount):                                           # Define DEPOSIT function with parameters account_balance and amount
    amount = float(input("How much would you like to deposit today?\n"))          # Accept user input for the deposit amount, in float format
    balance = account_balance + amount                                            # This addition assigns the updated value of the account balance, to the variable 'account_balance'
    print("Deposit was $%.2f , your new current balance is $%.2f" % (amount, balance))  # Prints deposit amount and account balance
    return balance                                                                # Return records the new value of account_balance to reflect accordingly in other transactions
    

  def withdrawal(account_balance, amount):                                        # Define WITHDRAWAL function with parameters account_balance and withdrawal_amount
    amount = float(input("How much would you like to withdraw today?\n"))         # Accept user input for the withdrawal amount, in float format
    if amount > account_balance:                                                  # Checking to see if the amount requested, is greater than the amount available
      print("Insufficient funds, $%.2f is greater than your account balance of $%.2f" % (amount, account_balance)) # If the amount requested is greater than the account balance, there are insufficient funds
    else:                                                                         # Sufficient amount of funds are available, the function continues
      balance = account_balance - amount                                          # Variable 'balance' is assigned to reflect the new available balance
      print ("Withdrawal amount was $%.2f, your new current balance is $%.2f" % (amount, balance))  # Prints withdrawal amount and account balance
      return balance                                                              # Return records the new value of balance to reflect accordingly in other transactions
  
# Lines 18 and 20 compose a conditional statement with the withdrawal function
# Line 18 => if the requested withdrawal amount is greater than the account balance, the conditional statement stops, and prints to the user there are insufficient funds
# Line 20 => if there are sufficient funds available, the conditional statement continues, updates the 'balance', and outputs to the user their withdrawal amount and new available balance

# ------------------------------------ ACCEPT USER INPUT - D, B, W, or Q -------------------------------------
userAccess = input ("Welcome to Tom & Kate Banking, if you would like to sign into your account, please press (C)ontinue, or (E)xit\n").upper()
if userAccess == 'C':
  transactionGo (pin)

  userChoice = 'go'                                                               # Setting the variable 'userChoice' to 'go', so we can implement a while loop

  # Step ONE => Create a WHILE loop to offer the user additional options after they have completed a transaction
  while userChoice != 'E':                                                        # As long as the user does not select 'E' (Exit), the program will keep looping with user choices
    
  # Step TWO =>  Ask user what action they would like to proceed with, user input is accepted and assigned to the variable 'userchoice'
    userChoice = input ("Would you like to check your (B)alance, make a (D)eposit, (W)ithdraw cash, or (E)xit?\n").upper()

  # Step THREE => conditional statement begins based on the value of variable 'userchoice' from user input
  # Four branches utilizing if / elif for DEPOSIT, BALANCE, WITHDRAWAL, EXIT
    if (userChoice == 'D'):                                                       # Accepts input D and proceeds with function 'deposit'
      deposit (account_balance, amount)                                           # DEPOSIT function is called with parameters 'balance' and 'amount'

    elif (userChoice == 'B'):                                                     # Accepts input B and proceeds with function 'balance'
      balance (account_balance)                                                   # BALANCE function is called with parameter 'balance'

    elif (userChoice == 'W'):                                                     # Accepts input D and proceeds with function 'withdrawal'
      withdrawal (account_balance, amount)                                        # WITHDRAWAL function is called with parameters 'balance' and 'amount'

    elif (userChoice == 'E'):                                                     # Accepts input E for EXIT
      print("Thank you for banking with us.")                                     # There is no function for EXIT, and therefore the user has a printed message ending their session

else:
  print("We hope to see you again soon, have a nice day!")



When I run this, the first function runs - transactionGo(pin), but when I go into the other transaction functions, IDLE tells me my variables are undefined.

5
  • Can you expand a bit more on "didn't work"? Did you get an error, or did nothing appear to happen, or was there some other unexpected output? Commented Jun 21, 2020 at 22:27
  • You have a mixture of global and local variables that have the same names. If you want to modify a global variable from inside a function, you must declare it global within that function, e.g. global amount. Otherwise assigning to it will assign to a local variable, leaving the global variable unchanged. Commented Jun 21, 2020 at 22:34
  • @halfer When I run this, the first function runs - transactionGo(pin), but when I go into the other transaction functions, IDLE tells me my variables are undefined. Commented Jun 21, 2020 at 22:35
  • @TomKarzes, thanks, that sounds in line with my problem. So I should assign account_balance as a global variable? Commented Jun 21, 2020 at 22:38
  • @MaryK39 Using a global is one way to do it. Another way would be to create a class and make it an attribute of the class. Commented Jun 21, 2020 at 22:39

1 Answer 1

1

PROBLEM: The scope of the account_balance variable and the functions within transactionGo are different to how you are using them. For example, if you wish to modify a variable outside of a function you need to declare it global at the top of your function definition.

Furthermore, the functions within transactionGo cannot be accessed anywhere in the file except for the transactionGo function itself as the scope of the functions is just within the transactionGo function and not anywhere else. This is why IDLE tells you the functions are undefined, because they are not a part of the file but a part of the transactionGo function and inside of that function only. So in your code those functions are never called

SOLUTIONS: You need to ensure you understand scope in python. The following link is an amazing explanation of what is meant by scope:

https://www.w3schools.com/python/python_scope.asp

In the following methods used to solve this issue, I will assume you want the functions themselves to change and modify the banking variables themselves.

Method #1: Using the 'global' keyword and fixing the functions scopes

# Describe the problem
# How to learn more about the problem
# Full solutions explained.

import sys

# -------------------------- DECLARE variables for balance, deposit, and withdrawal --------------------------

account_balance = float(500.25)                                                 # Starting balance indicated by Codio
#balance = 100
amount = 0
#deposit_amount = 0                                                              # Declare variable 'deposit_amount'
#withdrawal_amount = 0                                                           # Declare variable 'withdrawal_amount'
pin = ''

# -------------------------- DEFINE FUNCTIONS - balance, withdrawal, and deposit -----------------------------
def transactionGo (pin):
  pin = int(input("Please enter your 5 digit pin number:\n"))
  
def balance(account_balance):                                                   # Define balance function
  print("Your current balance is $%.2f" % (account_balance))                    # Prints the current available balance

def deposit():                                           # Define DEPOSIT function with parameters account_balance and amount
  global account_balance  # Using the 'global' keyword allows you to edit and modify the value of account_balance
  amount = float(input("How much would you like to deposit today?\n"))          # Accept user input for the deposit amount, in float format
  account_balance += amount                                           # This addition assigns the updated value of the account balance, to the variable 'account_balance'
  print("Deposit was $%.2f , your new current balance is $%.2f" % (amount, account_balance))  # Prints deposit amount and account balance                                                               # Return records the new value of account_balance to reflect accordingly in other transactions
    

def withdrawal():                                        # Define WITHDRAWAL function with parameters account_balance and withdrawal_amount
  global account_balance
  amount = float(input("How much would you like to withdraw today?\n"))         # Accept user input for the withdrawal amount, in float format
  if amount > account_balance:                                                  # Checking to see if the amount requested, is greater than the amount available
    print("Insufficient funds, $%.2f is greater than your account balance of $%.2f" % (amount, account_balance)) # If the amount requested is greater than the account balance, there are insufficient funds
  else:                                                                         # Sufficient amount of funds are available, the function continues
    account_balance -= amount                                          # Variable 'balance' is assigned to reflect the new available balance
    print ("Withdrawal amount was $%.2f, your new current balance is $%.2f" % (amount, account_balance))  # Prints withdrawal amount and account balance
    return balance                                                              # Return records the new value of balance to reflect accordingly in other transactions
  
# Lines 18 and 20 compose a conditional statement with the withdrawal function
# Line 18 => if the requested withdrawal amount is greater than the account balance, the conditional statement stops, and prints to the user there are insufficient funds
# Line 20 => if there are sufficient funds available, the conditional statement continues, updates the 'balance', and outputs to the user their withdrawal amount and new available balance

# ------------------------------------ ACCEPT USER INPUT - D, B, W, or Q -------------------------------------
userAccess = input ("Welcome to Tom & Kate Banking, if you would like to sign into your account, please press (C)ontinue, or (E)xit\n").upper()
if userAccess == 'C':
  transactionGo (pin)

  userChoice = 'go'                                                               # Setting the variable 'userChoice' to 'go', so we can implement a while loop

  # Step ONE => Create a WHILE loop to offer the user additional options after they have completed a transaction
  while userChoice != 'E':                                                        # As long as the user does not select 'E' (Exit), the program will keep looping with user choices
    
  # Step TWO =>  Ask user what action they would like to proceed with, user input is accepted and assigned to the variable 'userchoice'
    userChoice = input ("Would you like to check your (B)alance, make a (D)eposit, (W)ithdraw cash, or (E)xit?\n").upper()

  # Step THREE => conditional statement begins based on the value of variable 'userchoice' from user input
  # Four branches utilizing if / elif for DEPOSIT, BALANCE, WITHDRAWAL, EXIT
    if (userChoice == 'D'):                                                       # Accepts input D and proceeds with function 'deposit'
      deposit ()                                           # DEPOSIT function is called with parameters 'balance' and 'amount'

    elif (userChoice == 'B'):                                                     # Accepts input B and proceeds with function 'balance'
      balance (account_balance)                                                   # BALANCE function is called with parameter 'balance'

    elif (userChoice == 'W'):                                                     # Accepts input D and proceeds with function 'withdrawal'
      withdrawal ()                                        # WITHDRAWAL function is called with parameters 'balance' and 'amount'

    elif (userChoice == 'E'):                                                     # Accepts input E for EXIT
      print("Thank you for banking with us.")                                     # There is no function for EXIT, and therefore the user has a printed message ending their session

else:
  print("We hope to see you again soon, have a nice day!")

Method #2: Making a BankAccount class to store these methods and attributes

BankAccount.py

# This file will contain the class, to keep our project organised
class BankAccount:
    def __init__(self, account_balance, pin):
        self.account_balance = account_balance  # The account_balance is a class attribute
        self.pin = pin  # Class attribute

    def __repr__(self):
        return f'Account Balance: {self.account_balance}, Pin: {pin}'

    def check_pin(self):
        number_of_tries = 3  # Number of pin tries the user gets
        for i in range(number_of_tries, 0, -1):  # Counts backwards from the number_of_tries to the last digit 0
            print(f'Number of attempts remaining: {i}')
            while True:
                try:
                    inputted_pin = input('Please enter your 5 digit pin number:\n')

                    if len(inputted_pin) != 5:  # Checks and then handles whether or not the pin is 5 digits
                        incorrect_pin_length_error_message = 'Please ensure the pin you enter is 5 digits long\n\n'
                        print(incorrect_pin_length_error_message)

                    inputted_pin = int(inputted_pin)

                    if inputted_pin == self.pin:  # If the valid pin is the actual pin of this bank account
                        return True  # Allows the user to enter

                    else:
                        break  # Breaks out of the while loop making onto the next iteration of the for loop

                except ValueError:  # If the pin entered contains any character that is not a number then this except block will be hit when int(inputted_pin) is called
                    did_not_enter_all_numbers_error_message = 'Please ensure that you have entered pin number.\n\n'
                    print(did_not_enter_all_numbers_error_message)

                # If there is an error in the way a pin was entered it does not count as an attempt
                # Only when the pin is valid does it use up an attempt

        return False  # Returns False if the user fails to input the correct pin in the number of tries given

    def check_balance(self):
        """
        Prints the balance of the bank account.
        """
        print('Your current balance is $%.2f\n' % self.account_balance)

    def make_deposit(self):
        try:
            amount_to_deposit = input('How much would you like to deposit today?\n')
            try:
                if len(amount_to_deposit.split('.')[1]) != 2:  # Checks whether or not the user inputted two decimal places and two decimals only
                    more_than_two_decimal_place_error_message = 'Please ensure that your deposit is to two decimal places only.\n\n'
                    print(more_than_two_decimal_place_error_message)
                else:
                    if amount_to_deposit.count('.') > 1:  # Ensures that there is not more than one decimal point in the given input
                        multiple_decimal_points_error_message = 'Please ensure that the amount you wish to withdraw does not have multiple decimal points.\n\n'
                        print(multiple_decimal_points_error_message)
                    else:
                        self.account_balance += float(amount_to_deposit)  # Adds the amount once validated
                        self.check_balance()  # Prints out the current balance of the user

            except IndexError:  # Occurs if the user does not enter a decimal point
                did_not_enter_two_decimal_places_error_message = 'Please ensure that you enter exactly two decimal places in your deposit.\n\n'
                print(did_not_enter_two_decimal_places_error_message)

        except ValueError:  # Ensures that the input are all numbers
            did_not_enter_number_error_message = 'Please ensure that you enter a numerical amount.\n\n'

    def make_withdrawal(self):
        try:
            amount_to_withdraw = input('How much would you like to withdraw today?\n')
            if len(amount_to_withdraw.split('.')[1]) != 2:  # Ensures that there are two decimal places inputted and hta
                more_than_two_decimal_place_error_message = 'Please ensure that your withdrawal is to two decimal places only.\n\n'
                print(more_than_two_decimal_place_error_message)
            else:
                if amount_to_withdraw.count('.') > 1:  # Ensures that there is not more than one decimal point in the given input
                    multiple_decimal_points_error_message = 'Please ensure that the amount you wish to withdraw does not have multiple decimal points.\n\n'
                    print(multiple_decimal_points_error_message)
                else:
                    amount_to_withdraw = float(amount_to_withdraw)
                    if amount_to_withdraw > self.account_balance:  # Ensures that the user has enough funds to withdraw money from
                        insufficient_funds_message = 'Insufficient funds, $%.2f is greater than your account balance of $%.2f' % (amount_to_withdraw, self.account_balance)
                    else:
                        self.account_balance -= amount_to_withdraw  # Once validated withdraws the amount from the account
                        self.check_balance()  # Prints the user's balance


        except ValueError:  # Ensures that the input is only numbers
            did_not_enter_number_error_message = 'Please ensure that you enter a numerical amount.\n\n'

    def exit_transaction():
        exit_message = 'Thank you for banking with us.'
        print(exit_message)

    def do_transaction():
        # The main loop that you defined in your original code.
        if self.check_pin():

            while True:
                user_choice = input('Would you like to check your (B)alance, make a (D)eposit, (W)ithdraw cash, or (E)xit?\n').upper()

                if user_choice == 'B':
                    self.check_balance()

                elif user_choice == 'D':
                    self.make_deposit()

                elif user_choice == 'W':
                    self.make_withdrawal()

                elif user_choice == 'E':
                    self.exit_transaction()

        else:
            unsuccessful_pin_error_message = 'It seems like you ran out of pin tries, please try again later.'
            print(unsuccessful_pin_error_message)

main.py

from BankAccount import BankAccount

person1_bank_account = BankAccount(account_balance=500.25, pin=12345)
person2_bank_account = BankAccount(account_balance=1100.00, pin=54321)
# As many as you want

# Perform any functions on them
person1_bank_account.make_deposit()

person2_bank_account.make_withdrawal()

# Print values
print(person1_bank_account.account_balance)

print(person2_bank_account.pin)

This way your code is more flexible, clean, and scalable. For example in your class you can add ass many methods as you want in whatever way you want, and every instance of that class gains that functionality on top of being easy to manage. You can handle any exception you want in whatever way you want. So this is what I would suggest.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.