I almost explored all the key OOP concepts yesterday. Today I went through the remaining bits and pieces of Object Oriented Programming concepts and their implementation in Python. Along with that, I have tried to include some practical code exercises that involve the overall usage of OOP concepts in Python to recall all the concepts from the mental model that has been developed.
super()
super
is a reserved word in Python (was introduced in Python v 2.2) which comes into action during inheritance. When a subclass or child class which inherits from a parent class and needs to call a method of the parent class, it uses super
. I know this sounds quite confusing. So here's an example
- Without using
super
class Employee:
def __init__(self, name):
self.name = name
print(f'{self.name} is an employee')
class Manager(Employee):
def __init__(self, department, name):
self.department = department
self.name = name
Employee.__init__(self, name)
print(f'Manager, {self.department} department')
staff_1 = Manager('HR', 'Andy')
# Andy is an employee
# Manager, HR department
Here, the __init__
constructor method of the parent class is called by explicitly using the parent class name and then the self
object is passed as the first parameter.
- Using
super
(Compact syntax - No need to passself
)
class Employee:
def __init__(self, name):
self.name = name
print(f'{self.name} is an employee')
class Manager(Employee):
def __init__(self, department, name):
self.department = department
self.name = name
super().__init__(name)
print(f'Manager, {self.department} department')
staff_1 = Manager('HR', 'Andy')
# Andy is an employee
# Manager, HR department
Just like the constructor method shown in the above code, any method of the parent class can be called inside the child class using super()
In JavaScript, the syntax is more compact where super
is called like super(parameter). But I like the Python syntax as well. It is more explicit about calling the __init__
method using super
.
Introspection
Python is able to evaluate the type of an object (everything in Python is an object) at runtime. It means the interpreter is able to understand what are the properties and methods of the object and their accessibility at runtime dynamically. This is called introspection.
Python provides a built-in function dir
to introspect an object.
class Developer:
def __init__(self, name, language):
self.name = name
self.language = language
def introduce(self):
print(f'Hi! I am {self.name}. I code in {self.language}')
dev = Developer('Matt', 'Python')
print(dir(dev)) # Try this in any Python REPL
Dunder Methods
In Python, classes can be made more powerful by defining some magical methods called dunder methods. Dunder is a short name for double-under. These methods are prefixed and suffixed by double underscores __
. These special methods are predefined in Python for specific use cases. For example, we are able to access the built-in function because it is defined as a special dunder method __len__
.
When creating a class, these dunder methods can be used to simulate the behaviour of built-in types.
class Sentence:
words = []
def add_word(self, word):
self.words.append(word)
def __len__(self):
return len(self.words)
new_sentence = Sentence()
new_sentence.add_word('Hello')
new_sentence.add_word('World')
print(len(new_sentence))
I modified the Sentence class so that we can use the built-in method len
which is not available by default to implement custom logic. Dunder methods seem quite handy!
Multiple Inheritance
It is possible for a class to inherit properties and methods from multiple classes via Multiple Inheritance. It is a powerful concept but has its caveats as well. In comparison with JavaScript universe, Multiple Inheritance is not supported there.
class Batsman:
def swing_bat(self):
return 'What a shot!'
class Bowler:
def bowl_bouncer(self):
return 'What a bouncer!'
class AllRounder(Batsman, Bowler):
pass
player = AllRounder()
print(player.bowl_bouncer()) # What a shot!
print(player.swing_bat()) # What a bouncer!
It can get a bit complicated when parent classes have constructor methods that require initialization. In the child class, all the inherited class constructor methods need to be initialized.
class Batsman:
def __init__(self, hitting_power):
self.hitting_power = hitting_power
def swing_bat(self):
return f'Shot with power {self.hitting_power}'
class Bowler:
def __init__(self, delivery_speed):
self.delivery_speed = delivery_speed
def bowl_bouncer(self):
return f'Bowled with speed of {self.delivery_speed} kmph'
class AllRounder(Batsman, Bowler):
def __init__(self, hitting_power, delivery_speed):
Batsman.__init__(self, hitting_power)
Bowler.__init__(self, delivery_speed)
player = AllRounder(90, 80)
print(player.swing_bat())
print(player.bowl_bouncer())
Method Resolution Order
Method Resolution Order or mro in short, is the order in which properties and methods are inherited in Python.
When inheriting from multiple classes, the properties and methods are inherited by the child class in a specific hierarchy. The underlying algorithm that implements this in Python uses Depth first search algorithm.
class Employee:
secret_code = 'secret'
class Manager(Employee):
secret_code = 'm123'
class Accountant(Employee):
secret_code = 'a123'
class Owner(Manager, Accountant):
pass
person = Owner()
print(person.secret_code) # m123
To know the order of inheritance, Python provides a method mro
that can be called on the object to view the hierarchy of inheritance
print(Owner.mro()) # try in a python console with above code to see the result
Multiple inheritances can be difficult to understand so this pattern is not commonly used in practice. This is what I read in several articles.
That's all for today!
Finally done with the Object-Oriented Programming concepts in Python. The goal is to implement these principles when I start building real Python projects once this challenge is over.
I hope I was able to cover all the key Object Oriented Programming concepts in Python and share it without sounding very complicated.
Tomorrow will plunge into the territory of functional programming in Python. It's gonna be quite exciting for sure. Although Python, in general, is a Procedural Language and is popular for its Object-Oriented concepts, will explore how can functional style programming concepts be implemented in Python for the rest of this week.
Have a great one!
Top comments (5)
WOW! This is an eye opener. This blog is for keeps. I can imagine the research that went into this article. Keep up the great work.
Thank you :)
In the next coming few days, will you also touch on exception handling?
Yes that is there in my roadmap.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.