Vending Machine Project - Guidance on OOP code [Python] - python

Quite new to programming and have got an issue with some of my python code. I feel like there would be an easier way to write it/ simplify it. I am still working through it (working on the first item before and have got my 'tea' in the virtual vending machine. However I am not sure using that many if elif statements is the cleanest way to complete the code? Also, I want the code to constantly have an input available to the user so they can order more that one drink (if they have the money). So I would like it to loop and start again without losing the coin, how would I go about that?
I know it isn't much, but its my first assignment and I am happy that I have got it this far!
class Vending_Machine:
aussie_coins = (0.05, 0.10, 0.20, 0.50, 1.00, 2.00)
items = ['Tea','Coffee', 'Coke', 'Orange Juice']
item_price = [0.50, 1.00, 1.50, 1.00]
item_code = ['1', '2', '3', '4']
def __init__(self): #define total in vending machine.
self.total = 0.00
def insert_coin(self,coin):
if float(coin) not in (self.aussie_coins):
print ('The Vending Machine accepts only: {}. ' .format(self.aussie_coins), end = '')
else:
self.total += coin
print ('Currently there is a total of {: .2f} in machine' .format(self.total))
class interface(Vending_Machine):
def menu(self):
print("##################################")
print(" Welcome to my Vending Machine ")
print("All items below are readily available")
print(Vending_Machine.item_code[0], Vending_Machine.items[0], "${: .2f}".format(Vending_Machine.item_price[0])) #
print(Vending_Machine.item_code[1], Vending_Machine.items[1], "${: .2f}".format(Vending_Machine.item_price[1]))
print(Vending_Machine.item_code[2], Vending_Machine.items[2], "${: .2f}".format(Vending_Machine.item_price[2]))
print(Vending_Machine.item_code[3], Vending_Machine.items[3], "${: .2f}".format(Vending_Machine.item_price[3]))
print("##################################")
class user_input(interface):
def choice (self):
choice = input("Please enter the item code of an item you would like to purchase: ")
if choice == Vending_Machine.item_code[0]:
print ("You have selected {} - the price is ${: .2f}. Currently you have a total of ${: .2f} in the machine." .format(Vending_Machine.items[0], Vending_Machine.item_price[0], self.total))
if self.total < Vending_Machine.item_price[0]:
coins = float(input("Insert a coin into the vending machine: "))
Vending_Machine.insert_coin(self,coins)
if self.total == Vending_Machine.item_price[0]:
self.total -= Vending_Machine.item_price[0]
print('Please take your {}. There is currently ${: .2f} left in the Machine. Thanks, have a nice day!'.format(Vending_Machine.items[0], self.total))
elif self.total > Vending_Machine.item_price[0]:
self.total -= Vending_Machine.item_price[0]
print ('Please take your {}. There is currently ${: .2f} left in the Machine. Thanks, have a nice day!'.format(Vending_Machine.items[0], self.total))
elif self.total > Vending_Machine.item_price[0]:
self.total -= Vending_Machine.item_price[0]
print('Please take your {}. Total of {: .2f} in the machine' .format(Vending_Machine.items[0],self.total))
elif self.total == Vending_Machine.item_price[0]:
self.total -= Vending_Machine.item_price[0]
print('Please take your {}. Thanks, have a nice day!'.format(Vending_Machine.items[0]))
elif choice == Vending_Machine.item_code[1]:
print ("You have selected {} - the price is ${: .2f}. Currently you have a total of ${: .2f} in the machine." .format(Vending_Machine.items[1], Vending_Machine.item_price[1], self.total))
if self.total < Vending_Machine.item_price[1]:
insert_coin(input("Insert a coin into the vending machine: "))
elif self.total > Vending_Machine.item_price[1]:
self.total -= Vending_Machine.item_price[1]
print('Please take your {}. Total of {: .2f} in the machine' .format(Vending_Machine.items[1],self.total))
elif self.total == Vending_Machine.item_price[1]:
self.total -= Vending_Machine.item_price[1]
print('Please take your {}. Thanks, have a nice day!'.format(Vending_Machine.items[1]))
elif choice == Vending_Machine.item_code[2]:
print ("You have selected {} - the price is ${: .2f}. Currently you have a total of ${: .2f} in the machine." .format(Vending_Machine.items[2], Vending_Machine.item_price[2], self.total))
if self.total < Vending_Machine.item_price[2]:
insert_coin(input("Insert a coin into the vending machine: "))
elif self.total > Vending_Machine.item_price[2]:
self.total -= Vending_Machine.item_price[2]
print('Please take your {}. Total of {: .2f} in the machine' .format(Vending_Machine.items[2],self.total))
elif self.total == Vending_Machine.item_price[2]:
self.total -= Vending_Machine.item_price[2]
print('Please take your {}. Thanks, have a nice day!'.format(Vending_Machine.items[2]))
elif choice == Vending_Machine.item_code[3]:
print ("You have selected {} - the price is ${: .2f}. Currently you have a total of ${: .2f} in the machine." .format(Vending_Machine.items[3], Vending_Machine.item_price[3], self.total))
if self.total < Vending_Machine.item_price[3]: #if not the price of tea then..
insert_coin(input("Insert a coin into the vending machine: "))
elif self.total > Vending_Machine.item_price[3]:
self.total -= Vending_Machine.item_price[3]
print('Please take your {}. Total of {: .2f} in the machine' .format(Vending_Machine.items[3],self.total))
elif self.total == Vending_Machine.item_price[3]:
self.total -= Vending_Machine.item_price[3]
print('Please take your {}. Thanks, have a nice day!'.format(Vending_Machine.items[3]))
elif choice not in item_code:
print("Sorry we do not have item number {} available. Please try again" .format(choice))
vm = Vending_Machine()
i1 = interface()
u1 = user_input()
i1.menu()
u1.choice()

This is probably better suited for the code review board, but anyways...
Really great for a first project! That being said, you've already identified the major shortcoming of your code - you shouldn't have to use all those if/elifs. I also don't think you can justify wrapping most of your code into classes, like you've done. These are my suggestions:
Define two classes, VendingMachine and Item (an instance of this
class represents a single available item in the vending machine).
Follow good naming conventions. Notice the class names start with an
uppercase letter, and are camel-case.
Define an explicit entry point for your program, such as main.
Use a loop to iterate over your vending machine items when displaying
them. You can do something similar to cull all those if-statements.
Code:
class Item:
def __init__(self, name, price):
self.name = name
self.price = price
class VendingMachine:
def __init__(self):
self.items = [
Item("Tea", 0.50),
Item("Coffee", 1.00),
Item("Coke", 1.50),
Item("Orange Juice", 1.00)
]
self.money_inserted = 0.00
def display_items(self):
for code, item in enumerate(self.items, start=1):
print(f"[{code}] - {item.name} (${item.price:.2f})")
def insert_money(self, money):
if money <= 0.00:
raise ValueError
self.money_inserted += money
def main():
vending_machine = VendingMachine()
vending_machine.display_items()
while True:
try:
user_selection = int(input("Please enter the desired item code: "))
except ValueError:
continue
if user_selection in range(1, len(vending_machine.items)+1):
break
item = vending_machine.items[user_selection-1]
print(f"You've selected \"{item.name}\" - the price is ${item.price:.2f}")
while vending_machine.money_inserted < item.price:
print(f"You've inserted ${vending_machine.money_inserted:.2f} into the machine so far.")
while True:
try:
money_to_insert = float(input("Please enter the amount of money you'd like to insert: "))
vending_machine.insert_money(money_to_insert)
except ValueError:
continue
else:
break
print(f"Thank you! Please take your \"{item.name}\".")
print(f"The remaining change in the machine is ${vending_machine.money_inserted - item.price:.2f}.")
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
Output:
[1] - Tea ($0.50)
[2] - Coffee ($1.00)
[3] - Coke ($1.50)
[4] - Orange Juice ($1.00)
Please enter the desired item code: d32
Please enter the desired item code: 3
You've selected "Coke" - the price is $1.50
You've inserted $0.00 into the machine so far.
Please enter the amount of money you'd like to insert: 4ff4
Please enter the amount of money you'd like to insert: 1
You've inserted $1.00 into the machine so far.
Please enter the amount of money you'd like to insert: .10
You've inserted $1.10 into the machine so far.
Please enter the amount of money you'd like to insert: .90
Thank you! Please take your "Coke".
The remaining change in the machine is $0.50.
>>>

Good work. I've got one word to say to you: loops.
Where you've got 4 lines printing the vending machine items (at the start), you could replace it with a loop, like so:
for index in range(len(VendingMachine.items)):
print(Vending_Machine.item_code[index], Vending_Machine.items[index], "${: .2f}".format(Vending_Machine.item_price[index]))
Hopefully that gives you enough information to work out how to shrink the rest of your code too. If it needs to be elif (as in, you only want to check one of them, not all of them), use a break in the loop (see below for an example of a break).
As for your second question, you could have a loop around the insert_coin function call (in the user_input function) which keeps adding coins until the user inputs 'done'.
Here's a pretty basic example. There could be better ways to do this, but it should work.
while True:
coins = float(input("Insert a coin into the vending machine: "))
if coins == "done":
break
Vending_Machine.insert_coin(self,coins)

Related

How to print new balance after adding amount to the initial one?

I'm learning Python and went with a simple ATM code. I've tested it and everything works DownStream - what I mean by this is:
I have a few options when the class is initialized - Balance, Deposit, Withdraw, Exit.
When I run Balance I receive the amount set.
2.1. I go with Deposit - it shows the new amount the person has in their account
2.2. When I use Withdraw I get correct amount as well
Question - When I Deposit and then type Balance I'm getting the initial Balance of the user - that is expected. How can I change the code so after Depositing Money and select Balance to show me the new Balance?
Is this possible to be performed without much complicating the code?
The code:
class User:
def __init__(self):
self.fname = input('Enter your first name: ')
self.lname = input('Enter your last name: ')
self.age = input('Enter your age: ')
def user_details(self):
print('Details:')
print(f"First Name: {self.fname}")
print(f"Last Name: {self.lname}")
print(f"User age: {self.age}")
def deposit_money(self):
self.deposit_amount = 100
return self.deposit_amount
def withdraw_money(self, withdraw_amount):
self.withdraw_amount = withdraw_amount
return self.withdraw_amount
class ATM:
atm_balance = 10000
def __init__(self):
self.machine_balance = self.atm_balance
def user_bank_balance(self):
self.user_balance = 300
print ('Your current balance is ${}'.format(self.user_balance))
def deposit_atm(self, user):
self.total_savings = 0
deposit_m = float(input('How much do you want to deposit? '))
if deposit_m > user.deposit_money():
print('You do not have enough money to deposit')
elif deposit_m == user.deposit_money():
print('Amount deposited: ${}'.format(deposit_m))
self.total_savings = self.user_balance + deposit_m
print('Total amount in your account: ${}'.format(self.total_savings))
def withdraw_atm(self):
savings_left = 0
sum_to_withdraw = float(input('How much do you want to withdraw? '))
if self.atm_balance > sum_to_withdraw and self.user_balance > sum_to_withdraw:
savings_left = self.total_savings - sum_to_withdraw
print("You have withdraw {}".format(sum_to_withdraw))
print('You balance is {}'.format(savings_left))
elif self.atm_balance > sum_to_withdraw and self.user_balance < sum_to_withdraw:
print('Daily limit eceeded')
else:
print('ATM out of service')
class ATMUsage:
#classmethod
def run(cls):
print('Bulbank ATM')
instructions = print("""
Type 'Balance' to check your current balance,
Type 'Deposit' to deposit amount into your account,
Type 'Withdraw' to withdraw from your account,
Type 'Exit' to exit from your account,
""")
active = True
user1 = User()
atm1 = ATM()
user1.user_details()
while active:
selection = input("What would you like to do: 'Balance', 'Deposit', 'Withdraw', 'Exit': ")
if selection == 'Balance'.lower():
atm1.user_bank_balance()
elif selection == 'Deposit'.lower():
atm1.deposit_atm(user1)
elif selection == "Withdraw".lower():
atm1.withdraw_atm()
elif selection == 'Exit'.lower():
print('Thanks for passing by. Have a good one!')
break
else:
print('Wrong selection. Please, try again')
ATMUsage.run()
That's because every time you call the user_bank_balance method, you set the user_balance attribute to 300. So it wouldn't matter what updates you did on the user_balance, whenever you call the user_bank_balance method, you'll get 300
class ATM:
atm_balance = 10000
def __init__(self):
self.machine_balance = self.atm_balance
self.user_balance = 300
def user_bank_balance(self):
print ('Your current balance is ${}'.format(self.user_balance))

Function returns same value for each person

I'm working on a project for school to learn Python. In this project you are sitting at a restaurant and you program the bill. When I calculate the total of what everyone spend in a function and return the values. I only receive the total of the last person for every person.
If you run this code and use these values:
2
tim
4
4
james
12
12
you will see that they both get 69 in total.
I figured out if I press one time tab behind the "calculate_amount" to put it in the for loop you see it actually calculates the amounts correctly for the other people, but somehow it overwrites it when you put it out of the for loop
What I want is that after everyone told me how many drinks and cake they ate, I get a final report with all the totals of each person. Like I have right now but then with the correct totals.
I hope someone can steer me in the right direction thanks
#function makes first a calculation of the total each person has spend
def calculate_amount(amount_drinks, amount_cake):
count_number = 0
totaalFris = amount_drinks * drink_price
total_cake = amount_cake * cake_price
totaal = total_cake + totaalFris
totaal = str(totaal)
# then a for statement to print for each person the amount they have spend
for person in list_names:
count_number += 1
print(str(count_number) + " " + person + " " + str(totaal))
#prices of the food
drink_price = 2.50
cake_price = 3.25
#lists
list_names = []
drinked_sodas = []
eaten_food = []
count = 0
#while loop to check if there are coming people between 2-6
while True:
person_amount = int(input("How many people are coming? (2-6) "))
if person_amount < 2 or person_amount > 6:
print("error")
else: break
# ask for every person coming what their name, drinks and food is. and add that to the lists
for persoon in range(person_amount):
naam_persoon = str(input('\nWhat is your name? '))
list_names.append(naam_persoon)
glasses = int(input("How many drinks did you had? "))
drinked_sodas.append(glasses)
cake = int(input("how much cake did you eat? "))
eaten_food.append(cake)
count += 1
#calling the function which calculates the amount of glasses and cakes everyone had
#if u you put the function in this for statement it kinda works(press one time tab)
calculate_amount(glasses, cake)
#problem is I get same total amount for every person.
With the calculate_amount() function you have not parsed in the people buying drinks, therefore the function could not print the people that have bought the drinks so it just prints the value of the amount given multiple times.
Instead, you could try to create a function that takes the name, drinks, and cake variables and call the function for each person:
drink_price = 2.50
cake_price = 3.25
def singular_tab(name, glasses, cakes):
total_drinks = glasses * drink_price
total_cakes = cakes * cake_price
total = total_drinks + total_cakes
return print(f'name: {name}, tab: {total}')
while True:
person_amount = int(input("How many people are coming? (2-6) "))
if person_amount < 2 or person_amount > 6:
print("error")
else: break
for persoon in range(person_amount):
naam_persoon = str(input('\nWhat is your name? '))
glasses = int(input("How many drinks did you had? "))
cake = int(input("how much cake did you eat? "))
singular_tab(naam_persoon, glasses, cake)
To calculate the total amount of money spent by each person I utilized python dictionaries. With the dictionaries, I stored the name, amount of cakes, and amount of drinks.
After storing the relevant information in the dictionary I then parsed the dictionary into a function which then printed out each person's tab.
drink_price = 2.50
cake_price = 3.25
people_data = []
def tab_amount(dict):
final = []
final_text = ""
for people in dict:
cake_total = int(people[2]) * cake_price
glass_total = int(people[1]) * drink_price
person_total = cake_total + glass_total
final.append([people[0], person_total])
person_total = 0
for totals in final:
final_text += f'name: {totals[0]} spent in total: {totals[1]} euro\n'
return final_text
while True:
person_amount = int(input("How many people are coming? (2-6) "))
if person_amount < 2 or person_amount > 6:
print("error")
else: break
for person in range(person_amount):
name = str(input('\nWhat is your name? '))
glasses = int(input("How many drinks did you had? "))
cake = int(input("how much cake did you eat? "))
people_data.append([name, glasses, cake])
print(tab_amount(people_data))
Hope this helps :)

How to reduce repeated code with different variable names (python)

class Budget:
def __init__(self):
self.wants_perc = 0
self.wants_amt = 0
self.wants_left = 0
self.needs_perc = 0
self.needs_amt = 0
self.needs_left = 0
self.food_perc = 0
self.food_amt = 0
self.food_left = 0
while True:
try:
self.monthly_income = float(input("Enter your monthly income after taxes: "))
break
except ValueError:
print("Invalid Input : Please enter a number")
continue
while True:
print("Enter desired percentage of income spent for each category (do not include %): ")
try:
self.wants_perc = float(input("Wants: "))
self.needs_perc = float(input("Needs: "))
self.food_perc = float(input("Food: "))
if self.wants_perc + self.needs_perc + self.food_perc not in range(95, 105):
print("Invalid Input : Must add to 100%")
continue
else:
break
except ValueError:
print("Invalid Input : Please enter a number")
continue
def deposit(self):
dep_loc = input("Where would you like to deposit? ")
while True:
try:
if dep_loc.lower() == "wants":
self.wdep_amt = float(input("Deposit amount: "))
self.wants()
break
elif dep_loc.lower() == "needs":
self.ndep_amt = float(input("Deposit amount: "))
self.needs()
break
elif dep_loc.lower() == "food":
self.fdep_amt = float(input("Deposit amount: "))
self.food()
break
else:
print("Invalid Input")
break
except ValueError:
print("Invalid Input : Please enter a number")
continue
def wants(self):
self.wants_max = (self.wants_perc / 100) * self.monthly_income
self.wants_amt += self.wdep_amt
self.wants_left = self.wants_max - self.wants_amt
print(f"Amount spent on wants: ${self.wants_amt} \nAmount left to spend: ${round(self.wants_left,2)}")
def needs(self):
self.needs_max = (self.needs_perc / 100) * self.monthly_income
self.needs_amt += self.ndep_amt
self.needs_left = self.needs_max - self.needs_amt
print(f"Amount spent on needs: ${self.needs_amt} \nAmount left to spend: ${round(self.needs_left,2)}")
def food(self):
self.food_max = (self.food_perc / 100) * self.monthly_income
self.food_amt += self.fdep_amt
self.food_left = self.food_max - self.food_amt
print(f"Amount spent on food: ${self.food_amt} \nAmount left to spend: ${round(self.food_left,2)}")
wyatt = Budget()
while True:
wyatt.deposit()
I know this is a very general question, but is it possible to reduce the amount of repeated code I use? I feel like there has to be a way to use one general variable in a loop for each of the categories. My three functions that use the food, wants, and needs variables are all the exact same besides the names. I thought of getting user input and adding it to a list and indexing that list to get each category, but I couldn't fully figure it out. This may be too broad for stack overflow and if it is I apologize. Thanks!
In general there is no mechanism for reducing the amount of code - if there was, it would already be part of the language. Making a dictionary instead of member variables doesn't really help, because all it will do is replace your 9 member variables with 9 dictionary items.
However, there is an opportunity for factoring out repeated operations. Your instinct is correct that repeated blocks of code are a sign of poor design. I would suggest you start with a class structure something like this:
class BudgetItem:
def __init__(self, name):
self.name = name
self.perc = 0
self.amt = 0
self.left = 0
def get_perc(self):
self.perc = float(input(f"{self.name}: "))
class Budget:
def __init__(self, monthly_income):
self.monthly_income = monthly_income
self.wants = BudgetItem("Wants")
self.needs = BudgetItem("Needs")
self.food = BudgetItem("Food")
self.all_items = (self.wants, self.needs, self.food)
def gather_percentages(self):
print("Enter desired percentage of income"
" spent for each category (do not include %): ")
for b in self.all_items:
b.get_perc()
if 95.0 <= sum(b.perc for b in self.all_items) <= 105.0:
print("Precentages must sum to 100")
# etc.
Create a separate class to represent a budget category, since the logic for each is identical. Add methods to the little class to capture that logic. Now re-write your main class in terms of those three individual items. I won't take the time to refactor your whole program, but I hope you get the idea. You should end up with a much shorter program and no significant repeated logic.
One other thing: I think that putting an "input" statement in a class constructor is a terrible idea. Constructors should not contain complicated loops or extended logic. I would put it outside the class and pass the data into the class as arguments, or as method calls.

Extracting a tuple value from a dictionary in python

am working on a restaurant project, and I need a hand :)
so the program will display the main dishes, and the user has to enter which dish he/she wants..
and then the choice will be taken and then added to the bill (dish name, price, num of items)...
so far I chose a dictionary so when the user enters 1 (key), the program will display Mashroum Risto...
this what've created:
dishes = {1 : ('Mashroum Risito', 3.950), 2 : ['Tomato Pasta', 2.250],3:['Spagehtie',4.850]}
now my question is how to get the dish name without the price (the price is 3.950) and extract it! and also how to get the price without the name so then I can send it into the bill function to calculate it? and if you have any suggestions please go ahead, because i don't know if using dictionary was the right choice
def MainDish():
dishes = {1 : ('Mashroum Risito', 3.950), 2 : ['Tomato Pasta', 2.250],3:
['Spagehtie',4.850]}
dishes.values
print("1. Mashroum Risito 3.950KD")
print("2. Tomato Pasta 2.250KD")
print("3. Spagehtie 4.850KD")
choice = eval(input('Enter your choice: '))
NumOfItem = eval(input('How many dish(es): '))
while(choice != 0):
print(dishes.get(choice)) #to display the item only without the
price
a = dishes.values()
recipient(a)
break
The way you have implemented it:
print (dishes[1][0]) #will give you name
print (dishes[1][1]) #will give you price
where [x][y]
x = key in the dict (input in your case)
y = element of the value in the dict (0 = name, 1=price in your case)
You should probably create the dictionary better as below:
Follow up question is not so clear but I think this is what you are after roughly. You will need to tweak the returns to your usage:
def MainDish():
NumOfItem = float(input('How many dish(es): '))
dish = list(dishes)[choice-1]
cost_of_dish = dishes[dish]
totalcost = cost_of_dish * NumOfItem
print (f"\n\tOrdered {NumOfItem}x {dish} at {cost_of_dish}KD each. Total = {totalcost}KD\n")
return dish, cost_of_dish, totalcost
dishes = {'Mashroum Risito': 3.950, 'Tomato Pasta': 2.250}
for key,val in dishes.items():
print (f"{list(dishes).index(key)+1}. {key}: {val}KD")
keepgoing = True
while keepgoing:
choice = int(input('Enter your choice: '))
if choice == 0:
keepgoing = False
break
else:
dish, cost_of_dish, totalcost = MainDish()

Text Adeventure python sell system

Credits to https://stackoverflow.com/users/5003756/james for answering my previous post Python Text adventure Selling, check if its in list with
def sell(player_inventory):
print('Available to sell::')
for i, stuff in enumerate(player_inventory, 1):
print(i, stuff)
sold = input('what item number do you want to sell? ("q" to exit)')
if sold == 'q':
return player_inventory
try:
sold = int(sold)
except ValueError:
print('Please use the item number')
return player_inventory
if sold <= len(player_inventory):
print('successfully sold!!')
player_inventory.pop(sold - 1)
return player_inventory
So anyways, thanks to James we now have a sell function which runs perfectly fine. But the thing is, I want to make it so the way the money gets added to my
player_money
How I could add money in there? When I run the sell function, it does take the item away from my inventory, but doesn't add money to
player_money
if sold <= len(player_inventory):
print('successfully sold!!')
player_inventory.pop(sold - 1)
player_money(add + amount)
return player_inventory
you have to add a function where the items amount sold is stored and then that amount would be added to the function of the money in the players inventory.

Categories

Resources