'A confusing object composition python code

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!



Solution 1:[1]

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)

Solution 2:[2]

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.

Solution 3:[3]

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.

Solution 4:[4]

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.

Solution 5:[5]

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)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Noorullah Farid
Solution 3 Jakub G?siewski
Solution 4 Brayden Peoples
Solution 5