python modify dic deletion while iterating - python

method remove_item that requires similar arguments as add_item. It should remove items that have been added to the shopping cart and are not required. This method should deduct the cost of these items from the current total and also update the items dict accordingly. If the quantity of items to be removed exceeds current quantity in cart, assume that all entries of that item are to be removed.
class ShoppingCart(object):
#constructor
def __init__(self):
self.total = 0
self.items = {}
#method to add items in the shoping cart
def add_item(self,item_name, quantity, price):
self.total += (price * quantity )
self.items[item_name] = quantity
#method to remove items
def remove_item(self,item_name, quantity, price):
keys = self.items.keys()
for keys,v in self.items.items():
if keys == item_name and quantity > self.items[keys]: #if item_name equals iterated item
self.total -= (price * quantity )
del( self.items[keys] )
del(self.items[keys])
self.total -= (quantity * price)
And there are test units to check
def test_add_item_hidden(self):
self.cart.add_item('Mango', 3, 10)
self.cart.add_item('Orange', 16, 10)
self.assertEqual(self.cart.total, 190, msg='Cart total not correct after adding items')
self.assertEqual(self.cart.items['Orange'], 16, msg='Quantity of items not correct after adding item')
The method remove_item gives an error that dictionary changed size during iteration even after trying to access via keys as shown above

I think the remove_item function could be more simple.
#method to remove items
def remove_item(self,item_name, quantity, price):
if item_name in self.item.keys():
self.total -= (price * quantity)
del self.items[item_name]
With this version, you are checking if item_name is on the dictionary, and if so, you are removing it, and removing the price from total
Trying to remove from a dict,list,typle while iterating over them, is not usually a good idea.
If item_name is a list of keys, I have to suppose that quantity and price are too.
so a possible function could be:
def remove_item(self,item_name, quantity, price):
for index, value in enumerate(item_name):
if value in self.item.keys():
self.total -= (price[index] * quantity[index])
del self.items[value]
I haven't tested but should work.
if the idea is to get an inventory of the items. The function should be something like this.
#method to remove items
def remove_item(self,item_name, quantity, price):
if item_name in self.items.keys():
#remove from total the price for the items
if quantity <= self.items[item_name]:
self.total -= (price * quantity)
else:
self.total -= (price * self.items[item_name])
#if quantity is greater or equal of the current value, item is removed.
if quantity >= self.items[item_name]
del self.items[item_name]
else:
self.items[item_name] -= quantity

Related

Python class bug

My remove and new quantity methods dont work and I have no idea why. Here is my code:
# Type code for classes here
class ItemToPurchase():
def __init__(self):
self.item_description = 'none'
self.item_name = 'none'
self.item_price= 0
self.item_quantity = 0
def print_item_description(self):
print(f'{self.item_name}: {self.item_description}')
class ShoppingCart():
def __init__(self, customer_name='none', current_date='January 1, 2016'):
self.customer_name = customer_name
self.current_date = current_date
self.items = []
def add_item(self, item ):
''' Adds an item to cart_items list. Has parameter of type ItemToPurchase. Does not return anything. '''
self.items.append(item)
def remove_item(self, item ):
''' Removes item from cart_items list. Has a string (an item's name) parameter. Does not return anything.
If item name cannot be found, output this message: Item not found in cart. Nothing removed. '''
delete = False
for item in self.items:
if self.items == item:
to_be_deleted = self.items[item]
delete = True
else:
print("Item not found in cart. Nothing removed.")
if delete == True:
del self.items[to_be_deleted]
def modify_item(self, item, new_quantity):
'''Modifies an item's quantity. Has a parameter of type ItemToPurchase. Does not return anything.
If item can be found (by name) in cart, modify item in cart.'''
if item in self.items:
item.quantity = new_quantity
else:
print("Item not found in cart. Nothing modified.")
def get_num_items_in_cart(self):
'''Returns quantity of all items in cart. Has no parameters.'''
num_items = 0
for item in self.items:
num_items += item.item_quantity
#return the num_Items
return num_items
def get_cost_of_cart(self):
'''Determines and returns the total cost of items in cart. Has no parameters.'''
return sum(item.item_price * item.item_quantity for item in self.items)
def print_total(self):
'''Outputs total of objets in cart.
If cart is empty, outputs this message: CART IS EMPTY.'''
print(f"{self.customer_name}'s Shopping Cart - {self.current_date}")
number_items = self.get_num_items_in_cart()
print(f'Number of Items: {number_items}\n')
total_cost = self.get_cost_of_cart()
if total_cost == 0:
print("SHOPPING CART IS EMPTY\n")
print(f'Total: ${total_cost}')
return False
else:
for item in self.items:
item_cost = item.item_quantity * item.item_price
print(f'{item.item_name} {item.item_quantity} # ${item.item_price} = ${item_cost}')
print()
total_cost = self.get_cost_of_cart()
print(f'Total: ${total_cost}')
def print_descriptions(self):
''' Outputs each item's description'''
print(f"{self.customer_name}'s Shopping Cart - {self.current_date}\n")
for item in self.items:
print("Item Descriptions")
print(f'{item.item.name}: {item.item_description}')
def print_menu():
print("MENU\na - Add item to cart\nr - Remove item from cart\nc - Change item quantity\ni - Output items' descriptions\no - Output shopping cart\nq - Quit\n")
def execute_menu(choice , shopping_cart):
if choice == "o":
print("OUTPUT SHOPPING CART")
shopping_cart.print_total()
elif choice == "i":
print("OUTPUT ITEMS' CART")
shopping_cart.print_descriptions()
elif choice == "a":
print("ADD ITEM TO CART\nEnter the item name:")
item_name = input()
print("Enter the item description:")
item_description = input()
print("Enter the item price:")
item_price = int(input())
print("Enter the item quantity:")
item_quantity = int(input())
New_item = ItemToPurchase()
New_item.item_name = item_name
New_item.item_price = item_price
New_item.item_quantity = item_quantity
New_item.item_description = item_description
shopping_cart.add_item(New_item)
elif choice =="r":
print("REMOVE ITEM FROM CART\nEnter name of item to remove:")
removed_item = input()
shopping_cart.remove_item(removed_item)
elif choice == "i":
'''Implement Change item quantity menu option in execute_menu(). Hint: Make new ItemToPurchase object before using ModifyItem() method. '''
print("CHANGE ITEM QUANTITY\nEnter the item name:")
Item = ItemToPurchase()
Item.item_name = input()
print("Enter the new quantity:")
Item.item_quantity = input()
if __name__ == "__main__":
shopping_cart = ShoppingCart()
print("Enter customer's name:")
shopping_cart.customer_name = input()
print("Enter today's date:")
shopping_cart.current_date = input()
print()
print(f"Customer name: {shopping_cart.customer_name}\nToday's date: {shopping_cart.current_date}")
print()
print_menu()
print("Choose an option:")
while True:
choice = input()
if choice in "arcioq":
if choice == 'q':
break
else:
execute_menu(choice, shopping_cart)
print()
print_menu()
print("Choose an option:")
I think with remove I'm comparing wrong data types and i cant seem to make it work. By the way, the function names cannot change and i cannot add any new functions. I can only use doc strings of preexisting methods and classes and functions
You're not using del correctly. I think you've confused del with the .remove() method of a list.
del self.items[to_be_deleted] makes no sense. self.items is a list, and list indexes must be integers. But to_be_deleted is not an integer; it is an actual item in the list.
If you want to use del, you need the integer index of the list item to be removed.
But since you have the actual object itself, you can call self.items.remove() instead, which takes the actual object as an argument.

I want to create a simple inventory management system using classes and data structures. I am new to OOP in python

The code should Capture products in stock (i.e. name, price, date supplied, supplier name, quantity), Retrieve the price when name is given upon each purchase and deducts the quantity in
stock, Calculate the total price for each purchase and prints value indicating date and time of
purchase, Sends an alert when quantity reaches 5 to shopkeeper and places order to supplier.
My code right now is not looping so that I can add a number of products then be able to access them, I tried using a while loop but it is running forever. Kindly help
import datetime
class Stock:
def __init__(self, name, price, supplier, quantity,date):
self.name = name
self.price = price
self.date = date
self.supplier = supplier
self. quantity = quantity
def check_item(self, name):
for i in range(len(ls)):
if (ls[i].name == name):
return i
def sale(self):
n = int(input("How many products to sale: "))
total = 0
for j in range(n):
name = input("Enter Product name : ")
quantity = int(input("Enter quantity: "))
i = obj.check_item(name)
if i and ls[i].quantity >= quantity:
ls[i].quantity -= quantity
if ls[i].quantity < 5:
print("Low Stock! Low Stock! Order Placed")
obj.place_order(name, ls[i].supplier, quantity+10)
print("....Uncle G Shop....")
print(datetime.date.today())
print("Product Name | Quantity | Cost $")
print(ls[i].name, end=" ")
print(quantity, end=" ")
print(ls[i].price * quantity)
total += ls[i].price * quantity
print("\n")
print("Total Cost----->", "$" + total)
else:
print("Product out of stock or not enough quantity")
def purchase(self):
name = input("Enter Product name: ")
date = datetime.date.today()
i = obj.check_item(name)
if i:
ls[i].quantity += int(input("Enter quantity: "))
ls[i].price = int(input("Enter product price: "))
ls[i].date = date
else:
quantity = int(input("Enter quantity: "))
price = int(input("Enter product price: "))
supplier = input("Enter Supplier: ")
ob = Stock(name, price, supplier, quantity, date)
ls.append(ob)
def place_order(self,name, supplier, quantity):
return name, supplier, quantity
def print_products(self):
def __repr__(self):
return str(self.name) + str(self.price) + str(supplier) + str(self.quantity) + str(self.date)
return __repr__
def main(self):
print("Welcome To Uncle G Shop")
print("choose an option below")
print("\n1.Enter a Product\n2.Make a sale \n3.See all Products\n4.Exit")
option = int(input("Enter option here: "))
while True:
if option == 1:
obj.purchase()
elif option == 2:
obj.sale()
elif option == 3:
obj.print_products()
elif option == 4:
print("Have a good day")
break
else:
print("Enter a valid input!")
# A list to add Products
ls = []
# an object of Stock class
obj = Stock('', 0, 0, 0, '')
obj.main()
Your main menu doesn't have a break from the while loop for option 4.
You also need to think again about how to use your class. At present, you have multiple methods referring to a variable, ls, created outside of the class. Either treat Stock as a class to deal with individual purchase records, or to deal with stock overall. You could maintain in the class itself a list of instances of the class and offer class methods, as well as instance methods, to manage that overall stock.

Why won't these class attributes multiply?

I have to prompt the user for 2 items. For each Item the program needs to know the name, price, and quantity. Once received it outputs
'{Item name} {Item quantity} # ${Item price} = {Item total}'
The item total takes the price and multiplies it by the quantity. However I can't see the total in the output?'
class Item:
def __init__(self, name = 'none', price = 0, quantity = 0, total = 0):
self.item_name = name
self.item_price = price
self.item_quantity = quantity
self.total = price * quantity
def __add__(self, other):
return self.total + other.total
def print_item_cost(self):
return print('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
#Grab first item
item_1 = Item()
print('Item 1\nEnter the item name:')
item_1.item_name = input()
print('Enter the item price:')
item_1.item_price = input()
print('Enter the item quanity:')
item_1.item_quantity = input()
#Grab second item
item_2 = Item()
print('\nItem 2\nEnter the item name:')
item_2.item_name = input()
print('Enter the item price:')
item_2.item_price = input()
print('Enter the item quanity:')
item_2.item_quantity = input()
#Output cost
print('TOTAL COST\n', item_1.print_item_cost(), item_2.print_item_cost())
You're creating your items using the empty parameter list:
item_1 = Item()
so that default values are used in __init__: price = 0, quantity = 0, thus self.total is calculated as 0. Later, you change price and quantity properties of existing object:
item_2.item_price = input()
item_2.item_quantity = input()
but this does not change total. What you probably should do is:
#Grab first item
print('Item 1')
name = input('Enter the item name:')
price = input('Enter the item price:')
quantity = input('Enter the item quanity:')
item_1 = Item(name, price, quantity)
(and the same for item_2)
What you are doing in the following function is that you are returning a print of the statement. What you need to do is just return the statement and the print statment from where you made the call will print the returned output. Change from this
def print_item_cost(self):
return print('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
To this
def print_item_cost(self):
return('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
Edit:
Your total value does not change and stays the same once its initialized so add a new calculate total method
def calculate_total(self):
self.total = self.price * self.quantity
And call this method calculate_total() for each object to recalculate the total
A couple of things, you are setting the total price during initialization, therefore you have to set the item_price and item_quantity at __init__. Another thing when you accept number input from user you have to parse it appropriate data type, because by default it's string.
This would work as you intended it
class Item:
def __init__(self, name = 'none', price = 0, quantity = 0, total = 0):
self.item_name = name
self.item_price = price
self.item_quantity = quantity
self.total = price * quantity
def __add__(self, other):
return self.total + other.total
def print_item_cost(self):
return ('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
#Grab first item
print('Item 1\nEnter the item name:')
item_name = input()
print('Enter the item price:')
item_price = int(input())
print('Enter the item quanity:')
item_quantity = int(input())
item_1 = Item(item_name, item_price, item_quantity)
#Grab second item
print('\nItem 2\nEnter the item name:')
item_name = input()
print('Enter the item price:')
item_price = int(input())
print('Enter the item quanity:')
item_quantity = int(input())
item_2 = Item(item_name, item_price, item_quantity)
#Output cost
print('TOTAL COST\n', item_1.print_item_cost(), item_2.print_item_cost())
total must be calculated after you provide the actual values
input() returns string in python 3, should be converted to integers (or float)
changed the name of the print_item_cost function and removed print from the function; either print or return string but not both
class Item:
def __init__(self, name = 'none', price = 0, quantity = 0, total = 0):
self.item_name = name
self.item_price = price
self.item_quantity = quantity
# self.total = price * quantity
def total(self):
return self.item_price * self.item_quantity
def __add__(self, other):
return self.total + other.total
def item_cost_string(self):
return '{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total())
#Grab first item
item_1 = Item()
print('Item 1\nEnter the item name:')
item_1.item_name = input()
print('Enter the item price:')
item_1.item_price = int(input())
print('Enter the item quanity:')
item_1.item_quantity = int(input())
#Grab second item
item_2 = Item()
print('\nItem 2\nEnter the item name:')
item_2.item_name = input()
print('Enter the item price:')
item_2.item_price = int(input())
print('Enter the item quanity:')
item_2.item_quantity = int(input())
#Output cost
print('TOTAL COST\n', item_1.item_cost_string(), '\n', item_2.item_cost_string())
When you do something like
self.total = price * quantity
It uses the current values of price and quantity to set the value of self.total when this line is executed. This does not define a formula that will automatically update self.total when the value of price or quantity changes.

Calling Function in Function without Arguments

Quick question:
I want to call a main function without any arguments, and within the main function, I have several other functions that do have arguments. How would I go about doing this?
Here are the multiple functions:
# Takes the Portfolio dictionay, unpacks the multiple tuples, and calculates
# the total price of the shares at time of purchase
def total_purchase_price(portfolio):
totalprice = 0
totalpurprice = 0
for item in portfolio:
purdate, purprice, numshares, sym, curprice = item
totalprice += purprice * numshares
totalpurprice = totalprice
return totalpurprice
# Takes the Portfolio dictionay, unpacks the multiple tuples, and calculates
# the current total value of the shares
def total_value(portfolio):
totalprice = 0
totalvalueprice = 0
for item in portfolio:
purdate, purprice, numshares, sym, curprice = item
totalprice += curprice * numshares
totalvalueprice = totalprice
return totalvalueprice
# Takes the previous two functions, and subtracts them to get the total
# gain/lost of the shares
def total_gain(total_purchase_price, total_value, portfolio):
gainlost = total_value - total_purchase_price
return gainlost
ex. What I have right now (note: I know this won't work, just there for what I want, as each function returns a value):
def testQ1(total_purchase_price, total_value, total_gain, portfolio):
print("Total Cost =", total_purchase_price)
print("Current Value =", total_value)
print("Total gain/lost =", total_gain)
return
ex. What I want to achieve:
def testQ2():
total_purchase_price(portfolio)
total_value(portfolio)
total_gain(total_purchase_price, total_value, portfolio)
print("Total Cost =", total_purchase_price)
print("Current Value =", total_value)
print("Total gain/lost =", total_gain)
return
How would I do this? Thanks
Using a class might be easier:
class PortfolioParser:
def __init__(self, portfolio):
self.portfolio = portfolio
self.price = self.total_purchase_price()
self.value = self.total_value()
self.gain = self.total_gain()
def total_purchase_price(self):
# Takes the Portfolio dictionay, unpacks the multiple tuples, and calculates
# the total price of the shares at time of purchase
totalprice = 0
totalpurprice = 0
for item in self.portfolio:
purdate, purprice, numshares, sym, curprice = item
totalprice += purprice * numshares
totalpurprice = totalprice
return totalpurprice
def total_value(self):
# Takes the Portfolio dictionay, unpacks the multiple tuples, and calculates
# the current total value of the shares
totalprice = 0
totalvalueprice = 0
for item in self.portfolio:
purdate, purprice, numshares, sym, curprice = item
totalprice += curprice * numshares
totalvalueprice = totalprice
return totalvalueprice
def total_gain(self):
# Takes the previous two functions, and subtracts them to get the total
# gain/lost of the shares
return self.value- self.price
def testQ2(self):
print("Total Cost = {0}\nCurrent Value = {1}\nTotal Gains = {2}".format(self.price, self.value, self.gain))
And then you would use it like so:
myPortfolio = # The portfolio to parse
parser = PortfolioParser(myPortfolio)
parser.testQ2()
portfolio is the only argument that is not calculated within testQ2. You need to have a global value for it, or read it in (maybe in another function). After they have been calculated return the values in an appropriate order.
def testQ2():
portfolio = getPortfolio()
tp = total_purchase_price(portfolio)
tv = total_value(portfolio)
tg = total_gain(tp, tv, portfolio)
print("Total Cost =", tp)
print("Current Value =", tv)
print("Total gain/lost =", tg)
return portfolio, tp, tv, tg

Can't figure out a Python exercise with dictionaries

I have the following code:
shoppingList = ["banana","orange","apple"]
inventory = {"banana": 6,
"apple": 0,
"orange": 32,
"pear": 15
}
prices = {"banana": 4,
"apple": 2,
"orange": 1.5,
"pear": 3
}
def calculateBill(food):
total = 0
for k in food:
total += prices[k]
return total
calculateBill(shoppingList)
The exercise tells me to complete the function following these instructions:
Don't add the price of an article in your bill if it is not in your inventory.
After you buy an article, substract one from the inventory.
I don't know how to do it and I don't know if I have any other mistakes in my code.
If it isn't clear, the value in inventory is the stock of that item, and the value in "prices" is the price.
First of all, I don't see comida defined anywhere before its use. I'll assume that by comida, you mean food.
Here is a simple solution:
def calculateBill(food):
total = 0
for k in food:
if inventory.get(k, 0) > 0:
total += prices[k] # updates total
inventory[k] = inventory[k] - 1 # updates inventory
return total
You could do the following
def calculateBill(food):
total = 0
for k in food:
if k in inventory:
if inventory[k] > 0:
total += prices[k]
inventory[k] = inventory[k] - 1
else:
print 'There are no %s in stock' % k
else:
print 'dont stock %s' % k
return total
For 1)
if k in inventory:
Will check if the key is present in your inventory dict.
For 2)
inventory[k] = inventory[k] - 1
Will substract 1 from your inventory
One flaw in this code is that it does not check that the inventory count is above 0 before allowing to buy. So
if inventory[k] > 0:
Does this.
Here's a complete solution.
class Error(Exception):
"""Base class for Exceptions in this module"""
pass
class QtyError(Error):
"""Errors related to the quantity of food products ordered"""
pass
def calculateBill(food):
def buy_item(food_item, qty=1, inv_dict=None, prices_dict=None):
get_price = lambda item,price_dct: price_dct.get(item,9999999)
if inv_dict is None:
inv_dict = inventory
if prices_dict is None:
prices_dict = prices
if inv_dict.get(food_item, 0) >= qty:
inv_dict[food_item] -= qty
return sum(get_price(food_item, prices_dict) for _ in range(qty))
else:
raise QtyError("Cannot purchase item '{0}' of quantity {1}, inventory only contains {2} of '{0}'".format(food_item, qty, inv_dict.get(food_item,0)))
total = sum(buy_item(food_item, 1, inventory, prices) for food_item in food)
return total

Categories

Resources