DEV Community

Lakshmi Pritha Nadesan
Lakshmi Pritha Nadesan

Posted on

Day 34 - Constructor, Inheritance

Constructor:

In Python, a constructor is a special method that is automatically called when an object of a class is created. It is used to initialize the object's attributes with initial values. The constructor in Python is defined using the __init__ method.

class Employee:
    def __init__(self, name, department,job, year):
        pass

    def work(self):
        print("Working", self.empName, self.dept, self.desingnation,self.joining_year)

emp1 = Employee("guru", "B.Com", "Development", 2025)
emp2 = Employee("pritha", "M.E.,", "Design", 2025)
emp1.work()
Enter fullscreen mode Exit fullscreen mode
AttributeError: 'Employee' object has no attribute 'empName'
Enter fullscreen mode Exit fullscreen mode

We are trying to print instance variables like self.empName, self.dept, self.desingnation, and self.joining_year. However, these instance variables are not being initialized in the __init__ method.

class Employee:
    def __init__(self, name, department,job, year):
        self.empName = name
        self.dept = department
        self.designation=job
        self.joining_year = year

    def work(self):
        print("Working", self.empName, self.dept, self.designation,self.joining_year)

emp1 = Employee("guru", "B.Com", "Development", 2025)
emp2 = Employee("pritha", "M.E.,", "Design", 2025)
emp2.work()

Enter fullscreen mode Exit fullscreen mode
Working pritha M.E., Design 2025

Enter fullscreen mode Exit fullscreen mode

self.empName = name: The parameter name is assigned to the instance variable empName. This means the employee's name will be stored in the empName attribute of the object.

Similarly, self.dept = department, self.designation = job, and self.joining_year = year store the values for the employee's department, job, and joining year, respectively

The work() method simply prints the employee's details (name, department, job, and joining year).

class Employee:
    salary = 25000
    def __init__(self, name, department,job, year):
        self.empName = name
        self.dept = department
        self.desingnation=job
        self.joining_year = year

    def work(self):
        print("Working", self.empName, self.dept, self.desingnation,self.joining_year)
        print(self.salary)

    def take_leave(self):
        print(self.empName)

emp1 = Employee("guru", "B.Com", "Development", 2025)
emp2 = Employee("pritha", "M.E.,", "Design", 2025)
emp2.work()
emp1.take_leave()

Enter fullscreen mode Exit fullscreen mode
Working pritha M.E., Design 2025
25000
guru
Enter fullscreen mode Exit fullscreen mode

The salary is a class attribute, so if you want to access it within the work method, you need to access it from the class, either using self.salary or Employee.salary.

While you can use self.salary, it's important to understand that self refers to instance attributes, but since salary is a class attribute, accessing it via self is perfectly valid as well.

The take_leave() method is called for emp1 and prints the employee’s name.

class Employee:
    def __init__(self, name, department,job, year):
        self.name = name
        self.department = department
        self.job=job
        self.year = year
emp1 = Employee("guru", "B.Com", "Development", 2025)
emp2 = Employee("pritha", "M.E.,", "Design", 2025)

print(emp1.__dict__)
print(emp2.__dict__)
Enter fullscreen mode Exit fullscreen mode
{'name': 'guru', 'department': 'B.Com', 'job': 'Development', 'year': 2025}
{'name': 'pritha', 'department': 'M.E.,', 'job': 'Design', 'year': 2025}
Enter fullscreen mode Exit fullscreen mode

The __dict__ attribute contains the attributes of the object, as well as their current values. It is a dictionary that maps the attribute names to their corresponding values.

Calling the class method directly on the class using decorator:

class Employee:
    def __init__(self, name, department,job, year):
        self.name = name
        self.department = department
        self.job=job
        self.year = year
    @classmethod
    def credit_salary(cls):
        print("Credit salary on last day of every month")

emp1 = Employee("guru", "B.Com", "Development", 2025)
emp2 = Employee("pritha", "M.E.,", "Design", 2025)

Employee.credit_salary()
Enter fullscreen mode Exit fullscreen mode
Credit salary on last day of every month
Enter fullscreen mode Exit fullscreen mode

The @classmethod decorator is used to define a method that is bound to the class, not the instance. The first parameter of the class method is cls, which refers to the class itself, not the instance.

We can call credit_salary() using Employee.credit_salary(), because it is a class method and can be accessed from the class itself.

Inner class:

The inner class has access to the outer class's attributes and methods, but it behaves as a separate class with its own namespace.

class College:
    def __init__(self):
        print("College Constructor")

    class Dept:
        def __init__(self):
            print("Dept Constructor")

        def work(self):
            print("Working")

principal = College()
hod = principal.Dept()
hod.work()

Enter fullscreen mode Exit fullscreen mode
College Constructor
Dept Constructor
Working
Enter fullscreen mode Exit fullscreen mode

The Dept class is nested inside the College class, so it needs to be referenced as College.Dept when creating an instance of Dept outside the College class.

principal = College() initializes the College instance, and the constructor for College is called.

hod = College.Dept() creates an instance of the nested Dept class.

hod.work() calls the work method of the Dept class.

class SuperMarket:
    def __init__(self, product_name, price, discount):
        self.product_name = product_name
        self.price = price
        self.discount = discount

    def buy(self):
        print(self.product_name, self.price, self.discount)


product1 = SuperMarket("soap", 50, 10)
product2 = SuperMarket("Brush", 60,20)
product1.buy()
product2.buy()
Enter fullscreen mode Exit fullscreen mode
soap 50 10
Brush 60 20
Enter fullscreen mode Exit fullscreen mode

SuperMarket class with an__init__ method that accepts product_name, price, and discount as parameters.

product1 and product2 are instances of the SuperMarket class with their respective details.

The buy method prints the product_name, price, and discount for each product.

Constructor overloading:

In Python, constructor overloading is not directly supported. This is because Python does not allow multiple constructors with the same name.

However, you can achieve similar functionality by using default arguments or variable-length argument lists (e.g *args and **kwargs).

class SuperMarket:
    def __init__(self, product_name, price, *discount):
        self.product_name = product_name
        self.price = price
        self.discount = discount

    def buy(self):
        print(self.product_name, self.price, self.discount)


product1 = SuperMarket("soap", 50, 10)
product2 = SuperMarket("Brush", 60,20)
product3 = SuperMarket("Rice", 60)

product1.buy()
product2.buy()
product3.buy()
Enter fullscreen mode Exit fullscreen mode
soap 50 (10,)
Brush 60 (20,)
Rice 60 ()
Enter fullscreen mode Exit fullscreen mode

The *discount allows you to pass any number of discount values. If no value is provided for discount, it defaults to a tuple containing a single value 0.

If there are 3 arguments, it sets product_name, price, and discount.

If there are only 2 arguments, it assumes the discount is 0.

Inheritance:

Inheritance allows a class to inherit methods and properties from another class.

The class that inherits is called the child class (or subclass), and the class being inherited from is called the parent class (or superclass).

Types of Inheritance in Python:

There are five main types of inheritance in Python

Single Inheritance:In single inheritance, a child class inherits from one parent class.

Multiple Inheritance:In multiple inheritance, a class can inherit from more than one class.

Multilevel Inheritance:In multilevel inheritance, a class inherits from a class that is also a subclass of another class.

Hierarchical Inheritance:In hierarchical inheritance, multiple child classes inherit from a single parent class.

Hybrid Inheritance:Hybrid inheritance is a combination of multiple types of inheritance, such as a combination of multiple and multilevel inheritance.

class Shapes:
    def find_area(self, side1, side2):
        print(side1 * side2)
class Square(Shapes):
    pass

s = Square()
s.find_area(5,5)

class Rectangle(Shapes):
    pass
r = Rectangle()
r.find_area(10,8)
Enter fullscreen mode Exit fullscreen mode
25
80
Enter fullscreen mode Exit fullscreen mode

Both Square and Rectangle classes inherit from the Shapes class, which has the find_area method that calculates the area of a shape given two sides.

The Square class does not define its own find_area method but inherits it from the Shapes class. Therefore, calling s.find_area(5, 5) will calculate the area of a square with side length 5, which results in 5 * 5 = 25.

Similar to Rectangle class.

Example for multiple inheritance:

class Father:
    def work(self):
        print("Mechanical Engineer")

class Mother:
    def work(self):
        print("Software Engineer")

class Child(Father, Mother):
    pass

child = Child()
child.work()

Enter fullscreen mode Exit fullscreen mode
Mechanical Engineer
Enter fullscreen mode Exit fullscreen mode

Multiple Inheritance in Python, where the Child class inherits from both Father and Mother. The Child class can access methods from both parent classes, but when you call child.work(), Python follows a method resolution order (MRO) to determine which method to execute.

Method Resolution Order(MRO):

In Python, when multiple classes are involved in inheritance, the MRO determines the order in which methods are called.

Child inherits from Father first, and then from Mother.

Therefore, Python will call the work method from the Father class (because it appears first in the inheritance list).

class Father:
    def work(self):
        print("Mechanical Engineer")

class Mother:
    def work(self):
        print("Software Engineer")

class Child(Mother, Father):
    pass

child = Child()
child.work()
Enter fullscreen mode Exit fullscreen mode
Software Engineer
Enter fullscreen mode Exit fullscreen mode

If you want the Child class to inherit the work method from the Mother class, you can simply change the order of inheritance.

Method Overriding:

Method Overriding occurs when a subclass defines a method that has the same name, same parameters, and same return type as a method in its parent class. When the subclass calls this method, the method in the subclass overrides (replaces) the method in the parent class.

It is also called runtime polymorphism.

class Father:
    def work(self):
        print("Mechanical Engineer")

class Mother:
    def work(self):
        print("Software Engineer")

class Child(Mother, Father):
    def work(self):
        print("Business person")

child = Child()
child.work()
Enter fullscreen mode Exit fullscreen mode
Business person
Enter fullscreen mode Exit fullscreen mode

Overrides the work method and prints "Business person".

Since the Child class defines its own work method, the method from the Child class is called, regardless of the inheritance from Mother and Father.

Operator overriding:

Operator overloading allows you to define how operators like +, -, *, etc., should behave when they are used with instances of a class. In Python, this is done by defining special methods in your class, such as__add__, __sub__, __mul__ etc.

Image description

class Book:
    def __init__(self, pages):
        self.pages = pages

    def __add__(self, second):
        return self.pages + second.pages

book1 = Book(300)
book2 = Book(200)
print(book1 + book2)

Enter fullscreen mode Exit fullscreen mode
500
Enter fullscreen mode Exit fullscreen mode

__add__ method: This is the method that is called when the + operator is used between two Book objects. Inside this method, you're returning the sum of the pages attribute from both book1 and book2.

When you run print(book1 + book2), Python calls book1.__add__(book2) behind the scenes, which adds the pages attribute from both books together and prints the result.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def __mul__(self,other):
        return self.salary * other.days


class TimeSheet:
    def __init__(self, name, days):
        self.name = name
        self.days = days

emp = Employee("Pritha", 1000)
timesheet = TimeSheet("Pritha", 25)
print(emp * timesheet)

Enter fullscreen mode Exit fullscreen mode
25000
Enter fullscreen mode Exit fullscreen mode

When emp * timesheet is executed, Python internally calls emp.__mul__(timesheet).
The __mul__method multiplies the salary of the Employee object (emp) with the days from the TimeSheet object (timesheet).

Top comments (0)