Python test andela - python

HST2: OBJECT ORIENTED PROGRAMMING LAB
Create a class called ShoppingCart.
Create a constructor that takes no arguments and sets the total attribute to zero, and initializes an empty dict attribute named items.
Create a method add_item that requires item_name, quantity and price arguments. This method should add the cost of the added items to the current value of total. It should also add an entry to the items dict such that the key is the item_name and the value is the quantity of the item.
Create a 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 the removed items from the current total and also update the items dict accordingly.
If the quantity of an item to be removed exceeds the current quantity of that item in the cart, assume that all entries of that item are to be removed.
Create a method checkout that takes in cash_paid and returns the value of balance from the payment. If cash_paid is not enough to cover the total, return "Cash paid not enough".
Create a class called Shop that has a constructor which takes no arguments and initializes an attribute called quantity at 100.
Make sure Shop inherits from ShoppingCart.
In the Shop class, override the remove_item method, such that calling Shop's remove_item with no arguments decrements quantity by one.
# OOP Lab
class ShoppingCart(object):
def __init__(self):
total = 0
item = {}
self.total = total
self.item = item
def add_item(item_name, quantity, price):
cost = quantity * price
self.total += cost
self.item = {"item_name":quantity}
def remove_item(item_name,quantity,price):
cost = quantity * cost
self.total -= cost
for i in self.item:
if quantity > self.item[i]:
del self.item["item_name"]
def checkout(cash_paid):
if cash_paid < self.total:
return "Cash paid not enough"
class Shop(ShoppingCart):
def __init__(self):
quantity = 100
self.quantity = quantity
def remove_item():
self.quantity -= 1
#! Error State the following:
my add_item is having four argument instead of three each time i run this code:
Please i need help with this code, am new with python, i will appreciate any programming angel in python to rescue me now.

Try this, it should work out just fine:
class ShoppingCart(object):
def __init__(self):
self.total = 0
self.items = {}
def add_item(self, item_name, quantity, price):
self.total += quantity * price
if type(item_name) == str and quantity > 0:
self.items.update({item_name: quantity})
def remove_item(self, item_name, quantity, price):
if quantity >= self.items[item_name] and quantity >= 1:
items_cost = price * self.items[item_name]
self.total -= items_cost
del self.items[item_name]
else:
self.total -= quantity * price
self.items[item_name] -= quantity
def checkout(self, cash_paid):
balance = 0
if cash_paid < self.total:
return "Cash paid not enough"
balance = cash_paid - self.total
return balance
class Shop(ShoppingCart):
def __init__(self):
self.quantity = 100
def remove_item(self):
self.quantity -= 1

Class methods should accept self as the first argument so for example
def add_item(self, item_name, quantity, price):
Instead of
def add_item(item_name, quantity, price):
The "4th argument" being passed is implicitly self, that is why the number of arguments is one higher than you are expecting.

Related

Why do we pass arguments into a method instead of assigning them as attributes?

I am attempting to learn OOP in Python so I wanted to ask why do we pass parameters into the method when calling it, while the object is passed automatically by Python into the method as the first parameter and can be used to automatically identify and call its attributes instead of passing them when calling the method?
Why don't we do this:
class Item:
def calculate_total(self):
total = self.quantity * self.price
return total
item = Item()
item.price = 100
item.quantity = 5
item.calculate_total()
instead of this:
class Item:
def calculate_total(self, x, y):
total = x * y
return total
item = Item()
item.price = 100
item.quantity = 5
item.calculate_total(item.price, item.quantity)
The question is kinda flawed, as none of those should be seen in real-life code.
Attributes should be declared inside the class, preferrably at the moment of object initialisation.
class Item:
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
def calculate_total(self):
total = self.quantity * self.price
return total
item = Item(100, 5)
item.calculate_total()
This way we don't have a risk of self.price and self.quantity not being defined when we call calculate_total.
But, even with what you provided, why 2nd method would be worse quickly becomes apparent if you try to calculate things multiple times. Let's say you've got 3 slightly different totals (different currency maybe).
Would you rather write
item.calculate_total1(item.price, item.quantity)
item.calculate_total2(item.price, item.quantity)
item.calculate_total3(item.price, item.quantity)
or
item.calculate_total1()
item.calculate_total2()
item.calculate_total2()
?
As #Pranav Hosangadi mentions, if there are any parameters that do not have a place in the attributes of a class (e.g. discount, which can vary for different sales of the same item), that is where we would pass them to the method:
class Item:
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
def calculate_total(self, discount):
total = self.quantity * self.price * (1 - discount)
return total
item = Item(100, 5)
discount = 0.15
print(item.calculate_total(discount))

How do append an instance of a class?

I am looking to create a (very!) basic inventory management system
This is the brief:
Product Inventory Project - Create an application which manages an
inventory of products. Create a product class which has a price, id,
and quantity on hand. Then create an inventory class which keeps
track of various products and can sum up the inventory value.
Here is my code so far:
class Product:
def __init__(self, id_num, price, quantity):
self.price = price
self.id_num = id_num
self.quantity = quantity
class Inventory:
def __init__(self):
self.product_list = []
def add_item(self):
id_num = int(input('Enter id: '))
price = int(input('Enter price: '))
quantity = int(input('Enter quantity: '))
self.product_list.append(Product(id_num, price, quantity))
I don't understand how to make an instance of the product class append to the product list in testing. I feel like I am way off. Any help would be much appreciated!
The code is fine. You just need to execute :)
Look at this sample I just modified inputs and made static values for fast execution:
class Product:
def __init__(self, id_num, price, quantity):
self.price = price
self.id_num = id_num
self.quantity = quantity
class Inventory:
def __init__(self):
self.product_list = []
def add_item(self):
id_num = 1 #int(input('Enter id: '))
price = 100 #int(input('Enter price: '))
quantity = 4 #int(input('Enter quantity: '))
self.product_list.append(Product(id_num, price, quantity))
inv = Inventory()
inv.add_item()
print(inv.product_list[0].price)
You should get the print result of 100 which is the price of the item

Product Inventory program that takes products with an ID, quantity, and price and uses an Inventory class to keep track of the products

The Product class seems to work fine but I'm trying to figure out how to get the Inventory class to separate each product into there specific categories. I feel like I'm close but whenever I try and print out the inventory it just shows where it's stored in memory and doesn't actually print anything out. The output i receive when running is at the bottom. I want it to print out the actual products and data, not the instance of it stored in memory.
class Product:
def __init__(self, pid, price, quantity):
self.pid = pid
self.price = price
self.quantity = quantity
def __str__(self):
#Return the strinf representing the product
return "Product ID: {}\t Price: {}\t Quantity: {}\n".format(self.pid, self.price, self.quantity)
def get_id(self):
#returns id
return self.pid
def get_price(self):
#returns price
return self.price
def get_quantity(self):
#returns quantity
return self.quantity
def increase_quantity(self):
self.quantity += 1
def decrease_quantity(self):
self.quantity -= 1
def get_value(self):
value = self.quantity * self.price
return 'value is {}'.format(value)
product_1 = Product('fishing', 20, 10)
product_2 = Product('apparel', 35, 20)
class Inventory:
def __init__(self, products):
self.products = products
self.fishing_list = []
self.apparel_list = []
self.value = 0
def __repr__(self):
return "Inventory(products: {}, fishing_list: {}, apparel_list: {}, value: {})".format(self.products, self.fishing_list, self.apparel_list, self.value)
def add_fishing(self):
for product in self.products:
if product.get_id() == 'fishing':
self.fishing_list.append(product)
return '{} is in the fishing section'.format(self.fishing_list)
def add_apparel(self):
for product in self.products:
if product.get_id() == 'apparel':
self.apparel_list.append(product)
return '{} is in the apparel section'.format(self.apparel_list)
inventory_1 = Inventory([product_1, product_2])
inventory_1.add_fishing()
print(inventory_1)
OUTPUT = Inventory(products: [<main.Product instance at 0x10dbc8248>, <main.Product instance at 0x10dbc8290>], fishing_list: [<main.Product instance at 0x10dbc8248>], apparel_list: [], value: 0)
You need to specify how an object of the class Inventory should be printed.
To do this you need to implement at least one of the following functions in your class.
__repr__
__str__
This answer helps, which of both you should use: https://stackoverflow.com/a/2626364/8411228
An implementation could look something like this:
class Inventory:
# your code ...
def __repr__(self):
return str(self.products) + str(self.fishing_list) + str(self.apparel_list) + str(self.value)
# or even better with formatting
def __repr__(self):
return f"Inventory(products: {self.products}, fishing_list: {self.fishing_list}, apparel_list: {self.apparel_list}, value: {self.value})
Note that I used in the second example f strings, to format the output string.

Having issues with unit test in python

The code I wrote runs well without unit test. But fails when I unit test it.
Here is the source code
"""
shoppingcart.py
This program allows me to manage my shopping cart items
#autohor chuzksy
#version 2017-11-30
"""
class ShoppingCart(object):
"""This class manages shopping cart items in terms of
adding items, removing items and updating the cart list
"""
def __init__(self):
"""This is a constructor method belonging to the object of this ShoppingCart class"""
self.total = 0
self.items = {}
def add_item(self, item_name, quantity, price):
"""This method add items to the shopping cart dictionary"""
self.total = self.total + (quantity * price)
self.items.update({item_name: quantity})
def remove_item(self, item_name, quantity, price):
"""This method removes an item from the shopping cart and updates it"""
for name, qty in self.items.items():
if item_name == name:
if quantity < qty:
del self.items[item_name]
self.items.update({item_name: qty - quantity})
self.total -= (quantity * price)
else:
del self.items[item_name]
break
def checkout(self, cash_paid):
"""This method allows the user to pay for the items in the shopping cart"""
if cash_paid < self.total:
return "Cash paid not enough"
else:
return cash_paid - self.total
class Shop(ShoppingCart):
"""This is another class inheriting attributes and methods from the ShoppingCart class"""
def __init__(self):
super().__init__(self)
self.quantity = 100
def remove_item(self):
self.quantity -= 1
Here is the Unit test code:
import unittest
from shoppingcart import ShoppingCart
from shoppingcart import Shop
class ShoppingCartTestCases(unittest.TestCase):
def setUp(self):
self.cart = ShoppingCart()
self.shop = Shop()
def test_cart_property_initialization(self):
self.assertEqual(self.cart.total, 0, msg='Initial value of total not correct')
self.assertIsInstance(self.cart.items, dict, msg='Items is not a dictionary')
def test_add_item(self):
self.cart.add_item('Mango', 3, 10)
self.assertEqual(self.cart.total, 30, msg='Cart total not correct after adding items')
self.assertEqual(self.cart.items['Mango'], 3, msg='Quantity of items not correct after adding item')
def test_remove_item(self):
self.cart.add_item('Mango', 3, 10)
self.cart.remove_item('Mango', 2, 10)
self.assertEqual(self.cart.total, 10, msg='Cart total not correct after removing item')
self.assertEqual(self.cart.items['Mango'], 1, msg='Quantity of items not correct after removing item')
def test_checkout_returns_correct_balance(self):
self.cart.add_item('Mango', 3, 10)
self.cart.add_item('Orange', 16, 10)
self.assertEqual(self.cart.checkout(265), 75, msg='Balance of checkout not correct')
self.assertEqual(self.cart.checkout(25), 'Cash paid not enough', msg='Balance of checkout not correct')
def test_shop_is_instance_of_shopping_cart(self):
self.assertTrue(isinstance(self.shop, ShoppingCart), msg='Shop is not a subclass of ShoppingCart')
def test_shop_remove_item_method(self):
for i in range(15):
self.shop.remove_item()
self.assertEqual(self.shop.quantity, 85)
if __name__ == '__main__':
unittest.main(exit = False)
print("test pass")
Here is the output I get after running the unit test program
Thank you so much in advance.
This should help solve the problem with your subclass:
class Shop(ShoppingCart):
def __init__(self):
self.quantity = 100
def remove_item(self):
self.quantity = self.quantity - 1
return self.quantity
The error is correct. The ShoppingCartTestCases has not atttribute called "shop" unless you call the setUp method first. Are you sure that's being done?

Python keeping count of totals based on user input

I have a function in my item ordering system where the goal is to keep totals of how many items were ordered and print each item and the amount they were ordered
class Totals(object):
def __init__(self, total):
self.total = total
def total(order_amount, coffee_id):
count = 0
print("Coffee Type\t\tAmount of Times ordered")
print("-----------\t\t-----------------------")
for coffee in coffee_available:
if coffee.coffee_id == coffee_id:
count = order_amount
coffee_id = order_coffee
print("{}\t- - -\t {} ".format(coffee.coffee_type, count))
With this I can only print one item and it does show how many of that item is ordered but again it only does this to one item
The function is based on user input and the items are
coffee_available=[Coffee(1, "1: Flat White", 3.50),
Coffee(2, "2: Long Black", 3.50),
Coffee(3, "3: Cappuccino", 4.00),
Coffee(4, "4: Espresso", 3.25),
Coffee(5, "5: Latte", 3.50)]
how do I change the function so that it prints all items and keeps track of the amount of items ordered each time it is called so that after my code is looped through multiple times it still displays each item and the amount of times it was ordered
Ok now I have a method that prints each of the types of coffee but prints the amount of coffee ordered for 1 item to all of the items and it does not retain the amount of coffee ordered for each item as is needed
class Order(object):
def __init__(self):
self.total = {}
def order(self, order_amount, coffee_id):
if coffee_id not in self.total.keys():
self.total[coffee_id] = 0
self.total[coffee_id] += order_amount
def print_order(self, coffee_id):
print(" ")
print("Coffee Type\t\tAmount of Times ordered")
print("-----------\t\t-----------------------")
for coffee in coffee_available:
print("{}\t- - -\t {} ".format(coffee.coffee_type, self.total[coffee_id]))
and this is how I call it
new_order = Order()
new_order.order(order_amount, coffee_available[order_coffee - 1])
new_order.print_order(coffee_available[order_coffee - 1])
any suggestions would be great
You should save a dictionary that map from the coffee id to it amount of orders and update it on each order.
class Totals(object):
def __init__(self):
self.total = {}
def order(self, order_amount, coffee_id):
if coffee_id not in self.total.keys():
self.total[coffee_id] = 0
self.total[coffee_id] += order_amount
For the printing you should add a print function that print self.total as you wish.
This is personally how I believe you should manage the items ordered. This example has a dictionary mapping a Coffee object to the number of times it was ordered.
class Order(object):
ID = 0
def __init__(self):
self._orders = {}
self._order_id = Order.ID
Order.ID += 1
def add_item(self, coffee):
# Gets the current quantity for the coffee
# If no previous order qty defaults to 1
qty = self._orders.get(coffee, 1)
# Add / update dict with coffee object and qty
self._orders[coffee] = qty
def display_order(self):
print("Coffee Type\t\tAmount of Times ordered")
print("-----------\t\t-----------------------")
# For each coffee object ordered | sorted by coffee id
for coffee in sorted(self._orders, key = lambda item: item.coffee_id):
print("{:<10}\t- - -\t {} ".format(coffee.coffee_type,
coffee.price * self._orders[coffee]))

Categories

Resources