Python Topics : Inheritance
Inheritance Basics
classes are related to each other
an is-a relationship exists between a subclass (derived class) and a superclass (base class)
a has-a relationship exists between a class wwhich contains another class

inheritance uses an is-a relationship to inherit a class from a superclass
the subclass inherits all the superclass's attributes and methods, and extends the superclass's functionality

class SuperClass:
    ...
class SubClass(SuperClass):
    ...
Attribute Access
subclasses have access to the attributes inherited from the superclass
when the subclass's __init__() isn't explicitly defined, the superclass's __init__() method is called
when the subclass's __init__() is defined, the superclass's __init__() method is not called
accessing both types of attributes uses the same syntax

a subclass can have new instance attributes as well as those inherited from the superclass
explicitly defining a subclass's __init__() involves defining instance attributes and assigning instance attributes inherited from the superclass

class Employee:
    count = 0 # class attribute
    def  __init__(self):
        Employee.count += 1
        self.e_id = Employee.count
        self.hire_year = 2024

        def emp_display(self):
            print(f'Employee {self.e_id} was hired in {self.hire_year}')

class Developer(Employee):
    def  __init__(self):
        Employee.count += 1
        self.e_id = Employee.count
        self.hire_year = 2024
        self.lang_ep = ['Python', 'C#', 'Java']

    def dev_display(self):
        print(f'Proficient in {self.lang_xp}')
Methods
a subclass can override a superclass method by defining a method with the same signature as the superclass method
super() is a special method that provides a temporary superclass object instance for a subclass to use
super() is commonly used to call superclass methods from a subclass
super() is commonly used in a subclass's __init__() to assign inherited instance attributes
class SuperClass:
    cls_id = 0 # class attributes
    def __init__(self, name):
        SuperClass.cls_id += 1
        self.name = name


class SubClass(SuperClass):
    def __init__(self, name):
        super().__init__(name)
        self.e_id = SuperClass.cls_id

sub1 = SubClass('flip')
sub = SubClass('tracy')
print(f'SuperClass.id is {SuperClass.cls_id}') # 2
print(f'{sub1.name} has id {sub1.e_id}') # 1
print(f'{sub.name} has id {sub.e_id}') # 2
polymorphism is the concept of having many forms
with inheritance polymorphism is the basis of method overriding
polymorphism allows function overloading
Hierarchical Inheritance
hierarchical inheritance is a type of inheritance in which multiple classes inherit from a single superclass
multilevel inheritance is a type of inheritance in which a subclass becomes the superclass for another class
combining hierarchical and multilevel inheritance creates a tree-like organization of classes
Multiple Inheritance and Mixin Classes
multiple inheritance is a type of inheritance in which one class inherits from multiple classes
class A:
    def __init__(self, a_attr=2):
        self.a_attr = a_attr

class B:
    def __init__(self, b_attr=4):
        self.b_attr = b_attr

class C(A, B):
    def __init__(self, a_attr=5, b_attr=10, c_attr=20):
    A.__init__(self, a_attr)
    B.__init__(self, b_attr)
    self.c_attr = c_attr

b_inst = B(2)
c_inst = C(1, 2)
multiple inheritance should be implemented with care
the diamond problem occurs when a class inherits from multiple classes which share a common superclass
Example: Dessert and BakedGood both inherit from Food, and ApplePie inherits from Dessert and BakedGood

mixin classes promote modularity and can remove the diamond problem
a mixin class

  • has no superclass
  • has attributes and methods to be added to a subclass
  • isn't instantiated
Mixin classes are used in multiple inheritance to add functionality to a subclass without adding inheritance concerns
here the eat() method is not appropriate for the Rose class
class Plant:
    def photosynth(self):
        print("Photosynthesizing")
    def eat(self):
        print("Eating")

class Rose(Plant):
    pass
class Pitcher(Plant):
    pass
class VenusFlyTrap(Plant):
    pass
below Pitcher and VenusFlyTrap types use multiple inheritance to use the mixin class CarnivMixin
the mixin provides the eat() method to the two types
class Plant:
    def photosynth(self):
        print("Photosynthesizing")

class CarnivMixin:
    def eat(self):
        print("Eating")

class Rose(Plant):
    pass

class Pitcher(Plant, CarnivMixin):
    pass

class VenusFlyTrap(Plant, CarnivMixin)
    pass
index