pythonobject-composition

a realistic usage of composition in python


i have been reading about composition in python and after following some articles and answers here on stackoverlow i think i am able to get what it is and how it is implemented. but one question to which i am unable to find answer is why composition? (not comparing with benefits over inheritance). here is an example that i got from here:

class Salary:
    def __init__(self,pay):
        self.pay=pay

    def get_total(self):
       return (self.pay*12)

class Employee:
    def __init__(self,pay,bonus):
        self.pay=pay
        self.bonus=bonus
        self.obj_salary=Salary(self.pay)

    def annual_salary(self):
        return "Total: "  +  str(self.obj_salary.get_total()+self.bonus)


obj_emp=Employee(100,10)
print (obj_emp.annual_salary())

i would like to understand with a little realistic example where it would benefit by seperating two related classes( and what benefits exactly?)


Solution

  • using an extended version of your example

    class Salary:
        def __init__(self,pay):
            self.pay=pay
    
        def get_total(self):
           return (self.pay*12)
    
        def increase(self):
            self.pay *= 1.1
    
    
    class Employee:
        def __init__(self,pay,bonus):
            self.pay=pay
            self.bonus=bonus
            self.obj_salary=Salary(self.pay)
    
        def annual_salary(self):
            return "Total: "  +  str(self.obj_salary.get_total()+self.bonus)
    
    
    obj_emp=Employee(100,10)
    print (obj_emp.annual_salary())
    

    calling employee.salary.increase makes much more sense than employee.increase

    Also what if you needed multiple objects? would you inherit one of them or all of them leading to name possible name clashes?

    class Game:
        def __init__(self):
            self.screens       = [LoadingScreen, MapScreen]        
            self.player        = Player()
            self.display       = Display()
            self.stats         = Stats(self.display)
            self.screenIndex   = self.player.getScreen()
            self.currentScreen = self.screens[self.screenIndex]()
            self.run()
    

    * EDIT * after looking at this

    but on looking it up again i find that increase could be renamed to increaseSalary so employee.increaseSalary() would make sense right?
    

    you could simply move the increase to the employee class but what if you had a Manager class or a Boss class you would need to repeat the same code for each class

    class Employee:
        def __init__(self,pay,bonus):
            self.pay=pay
            self.bonus=bonus
    
        def annual_salary(self):
            return "Total: "  +  str(self.obj_salary.get_total()+self.bonus)
    
        def increase_salary(self):
            self.pay *= 1.1
    
    
    class Manager:
        def __init__(self,pay,bonus):
            self.pay=pay
            self.bonus=bonus
    
        def annual_salary(self):
            return "Total: "  +  str(self.obj_salary.get_total()+self.bonus)
    
        def increase_salary(self):
            self.pay *= 1.1
    
    class Boss:
        def __init__(self,pay,bonus):
            self.pay=pay
            self.bonus=bonus
    
        def annual_salary(self):
            return "Total: "  +  str(self.obj_salary.get_total()+self.bonus)
    
        def increase_salary(self):
            self.pay *= 1.1
    

    OR only once

    class Salary:
        def __init__(self,pay):
            self.pay=pay
    
        def get_total(self):
           return (self.pay*12)
    
        def increase(self, addition):
            self.pay *= addition
    

    you could also use a class methods to find average, max, min etc a lot easier