A confusing object composition python code - python

Currently, I am on an online crash course on python and I encountered a confusing code.
As shown below, it is a code that is designed to find out the number of cotton polo shirts.
class Clothing:
stock={ 'name': [],'material' :[], 'amount':[]}
def __init__(self,name):
material = ""
self.name = name
def add_item(self, name, material, amount):
Clothing.stock['name'].append(self.name)
Clothing.stock['material'].append(self.material)
Clothing.stock['amount'].append(amount)
def Stock_by_Material(self, material):
count=0
n=0
for item in Clothing.stock['material']:
if item == material:
count += Clothing.stock['amount'][n]
n+=1
return count
class shirt(Clothing):
material="Cotton"
class pants(Clothing):
material="Cotton"
polo = shirt("Polo")
sweatpants = pants("Sweatpants")
polo.add_item(polo.name, polo.material, 4)
sweatpants.add_item(sweatpants.name, sweatpants.material, 6)
current_stock = polo.Stock_by_Material("Cotton")
print(current_stock)
it is obvious that the number of cotton polo shirts is 4 and yet the code gives 10, the sum of the number of cotton polo and sweatpants, as the answer (which is considered a correct one actually).
My question is, shouldn't the polo.Stock_by_Material method only iterates elements in the dictionary in the instance "polo" instead of both "polo" and "sweatpants"? I mean "polo" and "sweatpants" are not even in the same class so how come the polo.Stock_by_Material method would count the amount of both classes?
Please forgive me if I made some stupid mistakes here. I am only 1 week into python without any prior programming experience. Many thanks!

You are aggregating by the material (Cotton). Both the shirt and sweatpants class has the material attribute set as Cotton. Hence there are 10 Cotton items, which is what you are displaying at the end.
If you want to aggregate by item, you could do as shown below.
class Clothing:
stock={ 'name': [],'material' :[], 'amount':[]}
def __init__(self,name):
material = ""
self.name = name
def add_item(self, name, material, amount):
Clothing.stock['name'].append(self.name)
Clothing.stock['material'].append(self.material)
Clothing.stock['amount'].append(amount)
def Stock_by_Material(self, material):
count=0
n=0
for item in Clothing.stock['material']:
if item == material:
count += Clothing.stock['amount'][n]
n+=1
return count
def Stock_by_item(self, name):
count=0
n=0
for rec in Clothing.stock['name']:
if rec == name:
count += Clothing.stock['amount'][n]
n+=1
return count
class shirt(Clothing):
material="Cotton"
class pants(Clothing):
material="Cotton"
polo = shirt("Polo")
other_polo_shirts = shirt("Polo")
sweatpants = pants("Sweatpants")
polo.add_item(polo.name, polo.material, 4)
other_polo_shirts.add_item(other_polo_shirts.name, other_polo_shirts.material, 16)
sweatpants.add_item(sweatpants.name, sweatpants.material, 6)
current_stock = polo.Stock_by_item("Polo")
print(current_stock)

If i got your question right,
stock is a static variable for the class Clothing. any children classes of this will share this variable.
Hence both polo and sweatpants share the same dictionary.
Hope that is helpful.

As #Sagi mention it returns all cotton stock as stock is shared between objects of Cloathing and its subclasses. Your confusion however is reasonable as this code breaks single responsibility principle, stock shouldn't be part of Clothing class.

Sagi is correct. The Stock_by_Material function needs to also check 'name' to make sure it is 'Polo', only then adding it to the count. You're not missing anything, the makers of the course just made an error.

In this conditional statement create problem in iteration so, try to comment it out. Sourly your program will run.
class Clothing:
stock={ 'name': [],'material' :[], 'amount':[]}
def __init__(self,name):
material = ""
self.name = name
def add_item(self, name, material, amount):
Clothing.stock['name'].append(self.name)
Clothing.stock['material'].append(self.material)
Clothing.stock['amount'].append(amount)
def Stock_by_Material(self, material):
count=0
n=0
for item in Clothing.stock['amount']:
# if item == material:
count += Clothing.stock['amount'][n]
n+=1
return count
class shirt(Clothing):
material="Cotton"
class pants(Clothing):
material="Cotton"
polo = shirt("Polo")
sweatpants = pants("Sweatpants")
polo.add_item(polo.name, polo.material, 4)
sweatpants.add_item(sweatpants.name, sweatpants.material, 6)
current_stock = polo.Stock_by_Material("Cotton")
print(current_stock)

Related

Sorting List of Objects python. Objects are of different class than the one I'm sorting in

I've seen similar questions but I just can't seem to figure this out. I have two classes, my Item class, and then my Receipt class. In Receipt I have a method read_file that reads a .txt file line by line, splitting it. I then append an Item object onto my list such that I have a list of Item objects. I'm trying to sort this list by price but I keep getting "AttributeError: type object 'Item' has no attribute 'price'" I've tried a few different things, and looked at similar answers on StackOverflow but I just can't seem to figure it out. From what I understand it's because it's looking at the class instead of the instance? Any help is appreciated, thank you.
The actual error is as follows:
Error message : items.sort(key=operator.attrgetter('price'),reverse=False)
AttributeError: type object 'Item' has no attribute 'price'
And my code:
import operator
import sys
class Item(object):
def __init__(self, category, name, quantity, price):
self.category = category
self.name = name
self.quantity = quantity
self.price = price
def getPrice(self):
return self.price;
class Receipt(object):
def __init__(self):
pass
def read_file(self):
with open('grocery.txt') as file:
items = [Item]
for line in file:
c,n,q,p = line.rstrip('\n').split(" ")
items.append(Item(c,n,q,float(p)))
return items
def ask_receipt_format(self):
answer = input("How would you like this receipt printed? (P for by price, C for by category, A for alphabetical order)")
if answer.capitalize() == 'P':
answer = 'P'
elif answer.capitalize() == 'C':
answer = 'C'
elif answer.capitalize() == 'A':
answer = 'A'
else:
print("You must choose a valid receipt format!\n")
self.ask_receipt_format()
return answer
def calculate_total(self):
pass
def print_bill(self, receipt_format,items):
if receipt_format == 'P':
print("Receipt by price")
print("Category Item Quantity Price Sub-Total")
items.sort(key=operator.attrgetter('price'),reverse=False)
for n in range(len(items)):
print("{:d} {0:4} {:d} {:4d} {:5d}".format(items[n].category,items[n].name,items[n].quantity,items[n].price, float(items[n].quantity) * float(items[n].price)))
def API(self):
self.print_bill(self.ask_receipt_format(),self.read_file())
def main():
receipt = Receipt()
receipt.API()
if __name__ == "__main__":
main()
Looking at this following snippet:
def read_file(self):
with open('grocery.txt') as file:
items = [Item]
The first thing you put in the list is the class itself, which doesn’t have the attribute price. Attributes are only passed on within instances of the class. Instead, you want to declare the list as empty: items = [].

Python PNL OOP, how to use attribute from another class

I am planning to design a program to track profit and loss of my stock account, then I used Python and hope to solve it in a Object Oriented way.
Code:
class PNL(object):
stock_amount = {}
def __init__(self,cash,position):
self.cash = cash
self.position = position
def buy(self,Stock,amount):
pass
def sell(self,Stock,amount):
pass
def stock_amt(self,Stock):
if Stock().symbol not in stock_amount:
stock_amount[Stock().symbol] = 0
else:
return stock_amount
class Stock():
def __init__(self,symbol,timestamp,price):
self.symbol = symbol
self.time = timestamp
self.price = price
a = PNL(0,0)
APPL = []
APPL.append(Stock('APPL',0,10))
APPL.append(Stock('APPL',1,12))
a.stock_amt('APPL')
for stock in APPL:
if stock.time == 0:
print stock.price
But this doesn't work fine, anyone has idea on that?
Firstly you need to fix the class PNL, when you declare the methods with Stock, as its an argument/parameter, you'd better choose another name, or write it in lowercase to make difference with the class Stock.
Just think you will give an instance to these methods, no need to write the type, and by the way, no need to instantiate again the class inside the method by doing Stock().symbol, you'll give an instance, or directly the attribute symbol if you prefer.
Also, the stock_amount can be stored as a instance attribute, as below :
class PNL(object):
def __init__(self,cash,position):
self.cash = cash
self.position = position
self.stock_amount = {}
def buy(self,stock,amount):
pass
def sell(self,stock,amount):
pass
def stock_amt(self,stock):
if stock.symbol not in self.stock_amount:
self.stock_amount[stock.symbol] = 0
else:
return self.stock_amount
Then when you call your classes, i think you wanted to loop on the list APPL you've built (then just call a.stock_amt(stock_object_created) :
a = PNL(0,0)
APPL = []
APPL.append(Stock('APPL1',0,10))
APPL.append(Stock('APPL2',1,12))
for stock in APPL:
a.stock_amt(stock)
if stock.time == 0:
print stock.price
print a.stock_amount
#>>>10
#>>>{'APPL2': 0, 'APPL1': 0}

Why is my code not working? (Beginner programmer, python 1st language)

I need some major improvements in my programming/coding and it's already been a month of this computer language field.
Right now I'm trying to create a class with 3 functions (lunch, breakfast, and dinner) and let's say I want to call the function lunch and add 'Strawberry' to the lunch list; it's supposed to add 1 to the list count (list_count) for amount of foods entered in the list count so far, and adds 'strawberry' to the dictionary.
So what I'm trying to do is I created a blank dictionary list (lunch_list) and created a starting count of food items (lunch_count)
So if I call lunch in the Food class, I'm trying to make the result like this:
list_count: 1
lunch_name: Strawberry
lunch_list = {1:'Strawberry'}
I was ready to write this script but after writing this I confused myself a lot more. I feel lost. This is going to be embarassing for me but here is my code:
class Food():
lunch_count = 0
lunch_list = {}
def __init__(self, food_name):
self.food_name = food_name
def lunch(self, lunch_count):
lunch_count += 1
lunch_list[lunch_count] = self.food_name
return lunch_list
strawberry = Food('Strawberry')
print strawberry.lunch('Strawberry')
class Food():
lunch_count = 0
lunch_list = {}
def __init__(self, food_name):
self.lc = None
self.food_name = food_name
def lunch(self):
Food.lunch_count += 1
self.lc = Food.lunch_count
Food.lunch_list[Food.lunch_count] = self.food_name
return Food.lunch_list
strawberry = Food('Strawberry')
result = strawberry.lunch()
print strawberry.lc
print strawberry.food_name
print result
Easier to read and understand.
lunch_count=0
lunch_item=""
lunch_menu={}
while lunch_item != "quit":
lunch_item=input("Enter Item: ")
menu_item=str(lunch_item)
lunch_count+=1
lunch_menu.update({lunch_count:menu_item})
print(lunch_menu)
else:
print("exiting")
Wrap this in a function and it adds an item and the count gets updated along with the newly entered string
Using python 3.5.2
If you like my answer please click the green arrow.
Thanks.
It doesn't seem like your 'Strawberry' parameter matches the lunch_count parameter in your lunch function. In the lunch method, you then can increment it by one every time you call lunch, rather than adding it as a parameter.
If you are new to Python, I would recommend Learn Python the Hard Way.
class Food():
lunch_count = 0
lunch_list = {}
def __init__(self, food_name):
self.food_name = food_name
def lunch(self):
Food.lunch_count += 1
Food.lunch_list[Food.lunch_count] = self.food_name
return Food.lunch_list
strawberry = Food('Strawberry')
print strawberry.lunch()

Using attributes from one class inside of another one

I'm working on a small fighting game as a learning experience and right now I'm working on implementing a store where you can buy weapons.
I decided to use a class for the store and have everything that you can do in it as a class method. But I'm unsure how to get all the data from my Weapon class and use it in the Store class. It's not pretty but here's what I have so far:
Sorry for misspelling tier.
class Item(object):
'''Anything that can be used or equiped.'''
def __init__(self, _id, desc, cost):
self._id = _id
self.desc = desc
self.cost = cost
class Weapon(Item):
def __init__(self, _id, desc, dam):
self._id = _id
self.desc = desc
self.dam = dam
def __str__(self):
return self._id
class Store(object):
dagger = Weapon('Dagger', 'A small knife. Weak but quick.', 'd4')
s_sword = Weapon('Short Sword', 'A small sword. Weak but quick.', 'd6')
l_sword = Weapon('Long Sword', 'A normal sword. Very versatile.', 'd8')
g_sword = Weapon('Great Sword', 'A powerful sword. Really heavy.', 'd10')
w_teir_1 = [dagger, s_sword, l_sword]
w_teir_2 = [w_teir_1, g_sword]
def intro(self):
print 'Welcome, what would you like to browse?'
print '(Items, weapons, armor)'
choice = raw_input(':> ')
if choice == 'weapons':
self.show_weapons(self.w_teir_1)
def show_weapons(self, teir):
for weapon in teir:
i = 1
print str(i), '.', teir._id
i += 1
raw_input()
I can't get the show_weapon function to print the _id for the weapon. All I can do is get it to print the raw object data.
Edit: I've figured out how to display the _id of the weapon when I'm passing the list w_teir_1 through the show_weapons method. But when I attempt to push w_teir_2 through, I get this error: AttributeError: 'list' object has no attribute '_id'
You need to change the last print stmt like below, since you're iterating over a list. _id attribute exists only for the elements which exists inside that list.
print str(i), '.', weapon._id
or
print str(i) + '.' + weapon._id
Update:
def show_weapons(self, teir):
for weapon in teir:
if isinstance(weapon, list):
for w in weapon:
i = 1
print str(i), '.', w._id
i += 1
raw_input()
else:
i = 1
print str(i), '.', weapon._id
i += 1
raw_input()

Python classes - help calling a method inside another class to update an attribute in an instance

Sorry if I'm vague in my question, first time posting on stackoverflow. I think I'm overlooking something really basic here, gone over several tutorials on classes but cannot for the life of me figure out where I'm going wrong. My code is the following:-
class catalogue(object):
def __init__(self, catalogueitem):
self.catalogueitem = catalogueitem
self.colors = []
self.stock = "No Stock"
def setStock(self, stock):
if self.stock == "No Stock":
self.stock = stock
class shop(object):
def __init__(self, items):
self.shopItems = []
for item in items:
self.shopItems.append(catalogue(item))
def setStock(self, stock, item="purse"):
self.shopItems.catalogue(item).setStock(stock)
newshop = shop( ["purse","handbag","wallet", "clutchbag"] )
newshop.setStock(10, "handbag")
Basically what I'm trying to do is call the method inside the class catalogue, from within the class shop, and update the item with a new stock value of 10 within the instance newshop. I think I'm lacking a basic understanding of how to do this, and I think I've overlooked something very basic, can anyone help me figure it out please?
Thanks
Betty
When you call
self.shopItems.catalogue(item).setStock(stock)
You're trying to call the methos catalogue of the object self.shopItems. The thing is, self.shopItems is a list. I think you should try with a code like this:
class catalogue(object):
def __init__(self, catalogueitem):
self.catalogueitem = catalogueitem
self.colors = []
self.stock = "No Stock"
def setStock(self, stock):
if self.stock == "No Stock":
self.stock = stock
class shop(object):
def __init__(self, items):
self.shopItems = []
for item in items:
self.shopItems.append(catalogue(item))
def setStock(self, stock, item="purse"):
c = catalogue(item) #Instance the catalogue class
c.setStock(stock) #Call the method setStock
self.shopitems.append(c) #this adds the items into the instance, but # the original objects created by __init__ are still there, i.e. they've not # updated just added.
newshop = shop( ["purse","handbag","wallet", "clutchbag"] )
newshop.setStock(10, "handbag")
Changed only the function setStock from shop class.
EDIT 1:
When you do self.shopItems = catalogue(item) you're overriding what you have
in the list. You should change that to:
self.shopItems.append(catalogue(item))
And call the method on the last element afterwars:
self.shopItems[-1].setStock(stock)
Result being:
def setStock(self, stock, item="purse"):
self.shopItems.append(catalogue(item))
self.shopItems[-1].setStock(stock)
EDIT 2:
Ok, to update the existent items you should first check if they exist, iterating through the list:
def setStock(self, stock, item="purse"):
c = catalogue(item) #Instance the catalogue class
c.setStock(stock) #Call the method setStock
index = -1
i = 0 #index
for shItem in self.shopItems:
if shItem.catalogueitem == item:
index = i #Saves the index to update
break #Breaks out of loop
i += 1
if index == -1:
self.shopitems.append(c) #index wasn't updated, the item is new
else:
self.shopitems[index] = (c) #replaces the item info
Firstly, you want to be able to look up shop items by name. An appropriate data structure to use for this is a dictionary (rather than a list). So, this changes the constructor for shop to:
def __init__(self, items):
self.shopItems = {}
for item in items:
self.shopItems[item] = catalogue(item)
Then, it is easy to update a shop item:
self.shopItems[item].setStock(stock)
Each shopItem is already a catalogue instance, so you can call setStock() on it directly. You should really check that there is such an item first, otherwise you will throw an exception. For example, if you call newshop.setStock(10, 'bag') -- there is no such item. You could protect against that as follows:
def setStock(self, stock, item="purse"):
if item not in self.shopItems:
self.shopItems[item] = catalogue(item)
self.shopItems[item].setStock(stock)

Categories

Resources