Two concepts to know in Inheritance.
## Example: Inherit the parent class and try calling its attributes/methods
class MyParent:
# class variables
some_var = 50
def __init__(self, para1):
self.para1 = para1
def some_func(self, num):
return num ** 2
def other_method(self, num):
return self.some_var - num
# Inherit 'MyParent' class
class MyChild(MyParent):
# Notice: The rounded brackets after the '<class_name>',
# this is where the parent class needs to be added, we added 'MyParent'
def __init__(self, arg1):
self.arg1 = arg1
# instantiate parent class inside child class by calling
# 'super().__init__()'
super().__init__(arg1)
# or pass 'self' to the class's constructor like this
# 'MyParent.__init__(self)'
def my_func(self, num):
# call parent's method using 'super()' function
output1 = super().some_func(num)
print(output1)
# access parent's variables using 'super()'
print(super().some_var)
# here 'self' calls parent's method, as this class doesn't have
# 'other_method()' this happens due to MRO, as we saw after child,
# parent class is the next lookup target
output2 = self.other_method(num)
print(output2)
def some_func(self, num):
return num ** 3
## Create a instance of child class
child_instance = MyChild(38)
# calling 'my_func()' calls child's method
child_instance.my_func(2) # 4 \
# 50 \
# 48
# calling same named method from parent & child, calls child's method
print(child_instance.some_func(2)) # 8
# now calling parent class's method, because child doesn't have this method
print(child_instance.other_method(8)) # 42
## Example: Inherit 'MyParent1' & 'MyParent2', try calling its
# attributes/methods
class MyParent1:
def __init__(self):
self.para1 = 10
def doing_something1(self, num):
return self.para1 - num
def other_method(self, num):
return num ** 2
class MyParent2:
def __init__(self):
self.para1 = 20
self.para2 = 42
def doing_something2(self, num):
return self.para1 - num
def other_method(self, num):
return num ** 3
## Inherit 'MyParent1' and 'MyParent2' classes
class MyChild(MyParent1, MyParent2):
def __init__(self, arg1):
self.arg1 = arg1
# initialize first parent using 'super()'
super().__init__()
print(self.para1)
# initialize second parent by passing 'self' object to its constructor
# because 'super()' can only keep track of a single class,
# which is 'MyParent1' here
MyParent2.__init__(self)
# so now 'MyParent2's variables/methods can be accessed
print(self.para1)
print(self.para2)
def my_func(self, num):
# here 'MyParent1's method will be called, due to MRO
output1 = self.other_method(num)
# similarly can do 'super().other_method(num)' or even by specific
# class name like this 'MyParent1.other_method(self)'
return output1
## Create a child instance
child = MyChild(30) # 10 \
# 20 \
# 42
# use the 'mro()' method on a instance to check the parent classes
print(MyChild.mro()) # [<class '__main__.MyChild'>, \
# <class '__main__.MyParent1'>, \
# <class '__main__.MyParent2'>, <class 'object'>]
# Same as before, calling 'MyParent1's method
print(child.other_method(2)) # 4
# now calling child's method
print(child.my_func(2)) # 4
## To call 'MyParent2's same named method using child instance
# call with the class name and pass child instance
print(MyParent2.other_method(child, 2)) # 8
print(MyParent1.other_method(child, 2)) # 4
# calling by class name is always available, you can use it to avoid
# naming collisions
object
class) and are not intended to be instantiated, only sub-classed.## Example: Create a class with printing functionality and then subclass it
class SomeMixin:
def printer(self):
print(f"a = {self.a}")
print(f"b = {self.b}")
print(f"total = {self.total}")
# can also have other methods
def do_something(self):
pass
# Now in below classes we won't be needing to write the printer function,
# instead we can just inherit 'SomeMixin' and we'll have the functionality
class Adder(SomeMixin):
def __init__(self, a, b):
self.a = a
self.b = b
def perform_op(self):
self.total = self.a + self.b
class Subbtr(SomeMixin):
def __init__(self, a, b):
self.a = a
self.b = b
def perform_op(self):
self.total = self.a - self.b
## Testing out printer
v = Adder(10, 20)
v.perform_op()
# call the printer method, but make sure variables share same names
# across the mixins and child classes
v.printer() # a = 10 \
# b = 20 \
# total = 30
v = Subbtr(40, 10)
v.perform_op()
v.printer() # a = 40 \
# b = 10 \
# total = 30
## Example: Inherit classes in a sequence like 'MyClass1' -> 'MyClass2'
# -> 'MyClass3'
class MyClass1:
def __init__(self):
self.para1 = 5
def doing_something1(self, num):
return self.para1 - num
def other_method(self, num):
return num ** 2
class MyClass2(MyClass1):
def __init__(self):
self.para2 = 20
# initialize parent class by class name
MyClass1.__init__(self)
def doing_something2(self, num):
return self.para2 - num
def other_method(self, num):
return num ** 3
class MyClass3(MyClass2):
def __init__(self):
self.my_para1 = 42
self.my_para2 = 42
# initialize parent class by class name
MyClass2.__init__(self)
def doing_something2(self, num):
return self.my_para1 - num
def other_method(self, num):
return num ** 3
# 'MyClass1' can access only its variables/methods
parent1 = MyClass1()
# check MRO
print(MyClass1.mro()) # [<class '__main__.MyClass1'>, <class 'object'>]
# 'MyClass2' can access its and 'MyClass1's variables/methods
parent2 = MyClass2()
print(MyClass2.mro()) # [<class '__main__.MyClass2'>, \
# <class '__main__.MyClass1'>, <class 'object'>]
# 'MyClass3' can access its, 'MyClass2's and 'MyClass1's variables/methods
child = MyClass3()
print(MyClass3.mro()) # [<class '__main__.MyClass3'>, \
# <class '__main__.MyClass2'>, \
# <class '__main__.MyClass1'>, <class 'object'>]
print(child.para1, child.para2, child.my_para1) # 5 20 42
## Example: Inherit a base class by 2 sub classes
class MyParent:
args = [22, 59, 81, 34, 73, 12, 35]
# inherit the 'MyParent' class
class MyChild1(MyParent):
def max_finder(self):
return max(self.args)
# inherit the 'MyParent' class
class MyChild2(MyParent):
def min_finder(self):
return min(self.args)
## Create the instances
my_instance1 = MyChild1()
my_instance2 = MyChild2()
print(my_instance1.max_finder()) # 81
print(my_instance2.min_finder()) # 12