简体   繁体   中英

In python, how do I update the balance when I withdraw money then deposit money?

I am creating an ATM using python code, and I need to create deposit and withdraw functions that behave just like an ATM would. Separately, the deposit and withdraw functions work. However, when I withdraw first then deposit, the balance does not update. Same thing when I deposit then withdraw.

Thank you and your help is much appreciated.

balance = 600

def withdraw():  # asks for withdrawal amount, withdraws amount from balance, returns the balance amount
    counter = 0
    while counter <= 2:
        while counter == 0:
            withdraw = int(input("Enter the amount you want to withdraw: AED "))
            counter = counter + 1
        while ((int(balance) - int(withdraw)) < 0):
            print("Error Amount not available in card.")
            withdraw = int(input("Please enter the amount you want to withdraw again: AED "))
            continue
        while ((float(balance) - float(withdraw)) >= 0):
            print("Amount left in your account: AED" + str(balance - withdraw))
            return (balance - withdraw)
        counter = counter + 1


def deposit():
    counter = 0
    while counter <= 2:
        while counter == 0:
            deposit = int(input("Enter amount to be deposited: "))
            counter = counter + 1
        while ((int(balance) + int(deposit)) >= 0):
            print("Amount left in your account: AED" + str(balance + deposit))
            return (balance + deposit)
        counter = counter + 1

withdraw()
deposit()

If I withdraw 17, the balance will be 583. However, when I deposit 12, the balance becomes 612, which is wrong it should be 595.

You are not changing the "balance" variable at all! Your code should like something like:

balance = withdraw()
balance = deposit()

But there are multiple other problems with your code. First of all you should not do that many casts. You have to convert your user input one time to a number, then just calculate everything with that type. Your are using float and int. If you want to stuff with currency, you should use probably decimal ( https://docs.python.org/2/library/decimal.html ), because floating point arithmetic is not exact for some special cases (you need to round) and integer does obviously not provide floating point arithmetic.

Also your special 'while' usage does not fit common coding standards and makes your code hard to read. Better write one function to get user input and seperate it from the withdrawal() and deposit() logic.

EDIT: Since you seem to be a beginner, I will provide a minimal working solution.

import decimal

balance = 600

def get_user_input(action):
  # Get user input and convert it to decimal type
  return decimal.Decimal(input("Please enter amount to {} from your account: ".format(action)))

def withdraw():
  amount = get_user_input("withdraw")
  return balance - amount

def deposit():
  amount = get_user_input("deposit")
  return balance + amount

print("Your Balance is {} AED".format(balance))
balance = withdraw()
balance = deposit()
print("Your Balance is {} AED".format(balance))

You just forgot to save the new balance, you just print it out

balance = 600

def withdraw():  # asks for withdrawal amount, withdraws amount from balance, returns the balance amount
    while True:
        withdraw = int(input("Enter amount to be withdrawn: "))
        if withdraw > balance:
            print("Error Amount not available in card.")
        else: 
            new_balance = balance - withdraw
            print("Amount left in your account: AED" + str(new_balance))
            return (new_balance)

def deposit():
    deposit = int(input("Enter amount to be deposited: "))
    new_balance = balance + deposit
    print("Amount left in your account: AED" + str(new_balance))
    return (new_balance)

# This is the only place you HAVE to change for it to work
balance = withdraw()
balance = deposit()   

Took the freedom to change it a bit, but most importantly, you should save the new balance.

I also suggest to make the integer conversion safer, by checking if it is an int before converting it to one.

        withdraw_string = input("Enter amount to be withdrawn: ")        
        try:
            withdraw_int = int(withdraw_string)
            is_int = True
        except ValueError:
            print("INVALID INPUT")
            is_int = False

        if is_int == True:

In both your deposit() and widthdraw() functions, you are never actually touching the variable that holds your balance, that's why you don't see the change.

You have defined the variable balance yet at no point do you ever update that value with balance = balance - x . You are only printing the outcome of that math operation with str(balance + deposit) , that code won't actually change your balance.

In order to change your balance you need to update that global variable with balance += widthdraw . But if you put that code in your code, you will get the following error:

UnboundLocalError: local variable 'balance' referenced before assignment

That is because in order to update a global variable from inside a function you need to use the global keyword so that a link is made to the global variable. Docs.

The following code now works, the two important lines are these:

balance -= withdraw
balance += deposit

That's how you are actually modifying the value in the balance variable instead of just seeing the output of the math operation.

balance = 600

def withdraw():  # asks for withdrawal amount, withdraws amount from balance, returns the balance amount
    global balance
    counter = 0
    while counter <= 2:
        while counter == 0:
            withdraw = int(input("Enter the amount you want to withdraw: AED "))
            counter = counter + 1
        while ((int(balance) - int(withdraw)) < 0):
            print("Error Amount not available in card.")
            withdraw = int(input("Please enter the amount you want to withdraw again: AED "))
            continue
        while ((float(balance) - float(withdraw)) >= 0):
            balance -= withdraw
            print("Amount left in your account: AED " + str(balance))
            return (balance)
        counter = counter + 1


def deposit():
    global balance
    counter = 0
    while counter <= 2:
        while counter == 0:
            deposit = int(input("Enter amount to be deposited: "))
            counter = counter + 1
        while ((int(balance) + int(deposit)) >= 0):
            balance += deposit
            print("Amount left in your account: AED" + str(balance))
            return balance
        counter = counter + 1

withdraw()
deposit()

Disclaimer : You can most definitely remove your return statements from withdraw and deposit since they are essentially useless in this way of solving the problem. Why useless? Because the global variable balance gets modified inside the methods. The return statements would be useful if instead you modified the balance outside the methods. Something like this:

balance = 600

def withdraw():  # asks for withdrawal amount, withdraws amount from balance, returns the balance amount
    counter = 0
    while counter <= 2:
        while counter == 0:
            withdraw = int(input("Enter the amount you want to withdraw: AED "))
            counter = counter + 1
        while ((int(balance) - int(withdraw)) < 0):
            print("Error Amount not available in card.")
            withdraw = int(input("Please enter the amount you want to withdraw again: AED "))
            continue
        while ((float(balance) - float(withdraw)) >= 0):
            tmp_balance -= withdraw
            print("Amount left in your account: AED " + str(tmp_balance))
            return tmp_balance
        counter = counter + 1


def deposit():
    counter = 0
    while counter <= 2:
        while counter == 0:
            deposit = int(input("Enter amount to be deposited: "))
            counter = counter + 1
        while ((int(balance) + int(deposit)) >= 0):
            tmp_balance += deposit
            print("Amount left in your account: AED" + str(tmp_balance))
            return tmp_balance
        counter = counter + 1

balance = withdraw()
balance = deposit()

In this second way, you are manipulating the value of balance based on the returns that the methods provide you. The difference here is that when you call withdraw or deposit you are not actually carrying that out, you won't truly see it reflected until you commit to it with balance = withdraw() . There's a good advantage to doing it this way, which is making sure that no exceptions occur and that if withdraw or deposit completely finished at 100% , then you can commit the changes.

I decided to go a bit further into your code, since you seem to have a whole lot of castings of ints , floats and while loops.

Here's an example of how it could be approached, in case it helps to give anyone more ideas: (remember, there's no single path with code, we all code differently)

  • Use a decorator to handle int() castings. You can read about decorators here: https://www.python.org/dev/peps/pep-0318/ . When you have repeated code (almost copy and paste), that's usually a sign to apply DRY (Don't Repeat Yourself). You can apply DRY by extracting the responsibility to a common function that you can call, or with awesome Python, use decorators. So decorators are nothing but functions, and you can "decorate" functions with them by simply putting @decorator_name . This just means that the decorator will be executed before the call to the decorated function.
  • I won't be casting to float because in your code you always cast the input to int() . If that's the case, then balance should never be a float.
  • Make a class called ATM that you can instantiate with a cash_available variable that is the cash in the ATM machine.
  • Remove fixed while loops (counter =2) and use instead one loop and let the user exit on command.
  • Extract the responsibility of getting the input for withdraw method, and let user decide whether they want to withdraw again.
  • Since you are using Python 3, apply f"" string format.

So now the code.

def exit_on_input_cast_error(func):
def wrapper(arg):
    try:
        return func(arg)
    except ValueError as ex:
        print("Exiting, bye.")
        exit()
return wrapper


class ATM():
"""A simple atm machine"""

def __init__(self, balance):
    self.cash_available = balance

@exit_on_input_cast_error
def withdraw(self):
    '''Withdraws entered amount, until user exits'''
    continue_withdraw = True
    while continue_withdraw:
        withdraw_amount = self._get_withdraw_input()
        self.cash_available -= withdraw_amount
        self.print_balance("left in")
        withdraw_again = str(input("Would you like to withdraw another amount? (Y or N)"))
        continue_withdraw = withdraw_again.lower() in ['1', 'y', 'yes']
    self.print_bye()

@exit_on_input_cast_error
def _get_withdraw_input(self):
    input_error = True
    while input_error:
        withdrawl = int(input("Enter the amount you want to withdraw (Press N to exit): AED "))

        if (self.cash_available - withdrawl) < 0:
            print("Error Amount not available in machine.")
            input_error = True
        elif (self.cash_available - withdrawl) > self.cash_available:
            print("Error, you can't withdraw a negative amount.")
            input_error = True
        else:
            input_error = False
    return withdrawl

@exit_on_input_cast_error
def deposit(self):
    input_error = True
    while input_error:
        depositing = int(input("Please enter the amount you want to deposit (Press N to exit): AED "))
        if (self.cash_available + depositing) < self.cash_available:
            print("You cannot deposit a negative amount.")
        else:
            input_error = False
            self.cash_available += depositing
    self.print_balance("now in")
    self.print_bye()

def print_balance(self, custom_insert = 'in'):
    print(f"Amount {custom_insert} your account: AED {self.cash_available}")

def print_bye(self):
    print("Thank you for using our services today, bye.")

Let's go through it bit by bit.

The decorator is this.:

def exit_on_input_cast_error(func):
    def wrapper(arg):
        try:
            return func(arg)
        except ValueError as ex:
            print("Exiting, bye.")
            exit()
    return wrapper

That's just the syntax for a decorator. The important part is the return func(arg) . That's the function that will get caught. So this decorator is merely in charge of catching a ValueError exception, which can be thrown when you try to cast something like a int('a') . This decorator is meant to prevent a user trying to withdraw or deposit strings into the atm machine. Since it's code that you will use for both input of withdrawing or depositing, I have placed it as a decorator for ease of call (hello DRY principle).

Next up we have the class constructor . I hope you are familiar with classes, if you are not, there's a lot of links and documentation you can look up so you understand the difference between a class and a method like you had originally. The biggest advantage in this case is that you can have multiple atm's with different amount of cash in each. Or you could instantiate an ATM class, and give it configuration like language, or coin type. Stuff like that.

class ATM():
"""A simple atm machine"""

def __init__(self, balance):
    self.cash_available = balance

That's such normal syntax for defining the class. The """A simple atm machine""" is the docstring so that when you call the classes .__doc__ you would get that in return.

Now to the good stuff.

@exit_on_input_cast_error
def _get_withdraw_input(self):
    input_error = True
    while input_error:
        withdrawl = int(input("Enter the amount you want to withdraw (Press N to exit): AED "))

        if (self.cash_available - withdrawl) < 0:
            print("Error Amount not available in machine.")
            input_error = True
        elif (self.cash_available - withdrawl) > self.cash_available:
            print("Error, you can't withdraw a negative amount.")
            input_error = True
        else:
            input_error = False
    return withdrawl

This method is meant to handle getting the input of the user. See how it's decorated with @exit_on_input_cast_error ? That means that if the user puts in 'a' , then the program exits. That's because the int(...) cast will throw that ValueError exception, that the decorator catches. The method does the following in pseudocode:

while there's an error do the following:
    Get the user input and cast it as an int. 
    Make sure the amount user has introduced matches the following criteria:
        It is not more than the cash available in the atm.
        It is not a negative number.

That's basically what this method does. It keeps asking the user to enter a valid input until either the user does so, or they exit by entering "N". And why do they exit when they enter "N". Because "N" is not an int , so when the cast occurs in this line:

withdrawl = int(input("Enter the amount you want to withdraw (Press N to exit): AED "))

A ValueError will be thrown by int() , that Exception is then caught by the handy decorator @exit_on_input_cast_error , which then prints "Bye" and exits for you. Cool right?

Next up is the actual withdraw method you had . The difference now is there is only one loop that continues to ask the user if he wants to keep withdrawing again once the action has finished. It's up to the user to either exit or withdraw again.

@exit_on_input_cast_error
def withdraw(self):
    '''Withdraws entered amount, until user exits'''
    continue_withdraw = True
    while continue_withdraw:
        withdraw_amount = self._get_withdraw_input()
        self.cash_available -= withdraw_amount
        self.print_balance("left in")
        withdraw_again = str(input("Would you like to withdraw another amount? (Y or N)"))
        continue_withdraw = withdraw_again.lower() in ['1', 'y', 'yes']
    self.print_bye()

In pseudocode:

while the user wants to withdraw:
    Get the user input
    Check that the withdraw action does not result in 0 or negative number. 
    Print the balance
    Ask the user if they want to withdraw again.

That's essentially what the method does. And it uses to new methods that are for printing message.

def print_balance(self, custom_insert = 'in'):
    print(f"Amount {custom_insert} your account: AED {self.cash_available}")

def print_bye(self):
    print("Thank you for using our services today, bye.")

These methods can be called and pass in custom parts of the message, like print_balance . They show the private class variable of the ATM class. In the withdraw method I have to point out that you can withdraw up to the point of reaching 0. The machine will let you try to keep withdrawing but it won't let you since there's 0 cash in it.

And last, the deposit method.

@exit_on_input_cast_error
def deposit(self):
    input_error = True
    while input_error:
        depositing = int(input("Please enter the amount you want to deposit (Press N to exit): AED "))
        if (self.cash_available + depositing) < self.cash_available:
            print("You cannot deposit a negative amount.")
        else:
            input_error = False
            self.cash_available += depositing
    self.print_balance("now in")
    self.print_bye()

As you can see, very simple and follows the same principle. Here's how you would call the actual methods:

atm_a = ATM(600)
atm_a.withdraw()

Here's an output of the code:

Enter the amount you want to withdraw (Press N to exit): AED 100
Amount left in your account: AED 500
Would you like to withdraw another amount? (Y or N)Y
Enter the amount you want to withdraw (Press N to exit): AED -1
Error, you can't withdraw a negative amount.
Enter the amount you want to withdraw (Press N to exit): AED 501
Error Amount not available in machine.
Enter the amount you want to withdraw (Press N to exit): AED 5
Amount left in your account: AED 495
Would you like to withdraw another amount? (Y or N)yes
Enter the amount you want to withdraw (Press N to exit): AED 5
Amount left in your account: AED 490
Would you like to withdraw another amount? (Y or N)no
Thank you for using our services today, bye.

If you want to withdraw and deposit:

atm_a = ATM(600)
atm_a.withdraw()
atm_a.deposit()

Here's output of the code:

Enter the amount you want to withdraw (Press N to exit): AED 500
Amount left in your account: AED 100
Would you like to withdraw another amount? (Y or N)no
Thank you for using our services today, bye.
Please enter the amount you want to deposit (Press N to exit): AED -1
You cannot deposit a negative amount.
Please enter the amount you want to deposit (Press N to exit): AED 1000
Amount now in your account: AED 1100
Thank you for using our services today, bye.

Notice how you can only deposit once and then it exists. That's because I didn't implement that, since I already did that for withdraw . Anyone can just replicate it in the deposit if they want.

I hope this wasn't too much and that I managed to explain it. There's loads of things than can be added to this:

  • Test cases
  • Difference between cash in ATM physically and balance of the user's account
  • In withdrawing, knowing if you have the right amount of bills to give the desired amount ...etc

I agree with @Chetan Ranpariya's suggestion, you haven't change your balance variable in your code for both function. You can change your balance variable using the expression balance += <increase amount> , balance -= <decrease amount> or balance = balance + <increase amount> , etc.

balance = 600

def withdraw():  # asks for withdrawal amount, withdraws amount from balance, returns the balance amount
    counter = 0
    while counter <= 2:
        while counter == 0:
            withdraw = int(input("Enter the amount you want to withdraw: AED "))
            counter = counter + 1
        while ((int(balance) - int(withdraw)) < 0):
            print("Error Amount not available in card.")
            withdraw = int(input("Please enter the amount you want to withdraw again: AED "))
            continue
        while ((float(balance) - float(withdraw)) >= 0):
            print("Amount left in your account: AED" + str(balance - withdraw))
            return (balance - withdraw)
        counter = counter + 1


def deposit():
    counter = 0
    while counter <= 2:
        while counter == 0:
            deposit = int(input("Enter amount to be deposited: "))
            counter = counter + 1
        while ((int(balance) + int(deposit)) >= 0):
            print("Amount left in your account: AED" + str(balance + deposit))
            return (balance + deposit)
        counter = counter + 1

balance = withdraw()
balance = deposit()

You are not changing the balance variable, you are only returning the value with the added or reduced deposit or withdraw .

Try changing your code so that it saves the new balance before returning it. So instead of:

print("Amount left in your account: AED" + str(balance - withdraw))
return (balance - withdraw)

Try:

balance = (balance - withdraw)
print("Amount left in your account: AED" + str(balance))
return balance

Then do the same with the deposit function.

Your new code:

balance = 600

def withdraw():  # asks for withdrawal amount, withdraws amount from balance, returns the balance amount
    counter = 0
    while counter <= 2:
        while counter == 0:
            withdraw = int(input("Enter the amount you want to withdraw: AED "))
            counter = counter + 1
        while ((int(balance) - int(withdraw)) < 0):
            print("Error Amount not available in card.")
            withdraw = int(input("Please enter the amount you want to withdraw again: AED "))
            continue
        while ((float(balance) - float(withdraw)) >= 0):
            balance = (balance - withdraw)
            print("Amount left in your account: AED" + str(balance))
            return balance
        counter = counter + 1


def deposit():
    counter = 0
    while counter <= 2:
        while counter == 0:
            deposit = int(input("Enter amount to be deposited: "))
            counter = counter + 1
        while ((int(balance) + int(deposit)) >= 0):
            balance = (balance + deposit)
            print("Amount left in your account: AED" + str(balance))
            return balance
        counter = counter + 1

withdraw()
deposit()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM