Inheritance in Python is the property of classes to inherit from other classes. A class can inherit objects, attributes and methods from another class – called the superclass. The class which inherits from a superclass is called a subclass or child class. While inheritance is the property of Object orient programming but there is a support for multiple inheritance in Python as well.
Inheritance In Python
Syntax & Example
class BaseClass: Body of base class class DerivedClass(BaseClass): Body of derived class
Let’s take a simple example – we create a Person class with the two attributes “firstname” and “lastname” with one method – Name method which returns the values, but we don’t have an attribute name. This method creates an output by creating it from more than one private attribute. Name returns the concatenation of the first name and last name of the person.
Now we need another sub class, which inherits from Person. Just like us, we know employees in companies are Persons and if we create an Employee class without inheriting from Person, we need to define all the attributes and methods in the Employee class again. This means we would create a design and a data redundancy. To avoid such, we can make Employee to inherit attributes from Person.
class Person: def __init__(self, first, last): self.firstname = first self.lastname = last def Name(self): return self.firstname + " " + self.lastname class Employee(Person): def __init__(self, first, last, staffnum): Person.__init__(self,first, last) self.staffnumber = staffnum def GetEmployee(self): return self.Name() + ", " + self.staffnumber x = Person("Jon", "Snow") y = Employee("Cersei", "Lannister", "266") print(x.Name()) print(y.GetEmployee())
Jon Snow Cersei Lannister, 266
The __init__ method of the Employee class explicitly invokes the __init__method of the Person class. Instead we can also use super – super().__init__(first, last) is automatically replaced by a call to the superclasses method, in this case __init__:
def __init__(self, first, last, staffnum): super().__init__(first, last) self.staffnumber = staffnum
Only Python 3 supports super()) without arguments for lesser versions of Python use “super(Employee, self).__init__(first, last, age)”
Overloading and Overriding
Instead of using the methods “Name” and “GetEmployee” in our previous example, it might have been better to put this functionality into the “__str__” method. In doing so, we gain a lot, especially a leaner design. We have a string casting for our classes and we can simply print out instances. Let’s start with a __str__ method in Person:
class Person: def __init__(self, first, last): self.firstname = first self.lastname = last def __str__(self): return self.firstname + " " + self.lastname class Employee(Person): def __init__(self, first, last, staffnum): super().__init__(first, last) self.staffnumber = staffnum x = Person("Jon", "Snow") y = Employee("Cersei", "Lannister", "266") print(x) print(y)
Jon Snow Cersei Lannister
We see that if we print an instance of the Employee class, the __str__ method of Person is used. This is due to inheritance. The only problem we have now is the fact that the output of “print(y)” is not the same as the “print(y.GetEmployee())”. This means that our Employee class needs its own __str__ method. We could write it like this:
def __str__(self): return self.firstname + " " + self.lastname + ", " + self.staffnumber
But it is a lot better to use the __str__ method of Person inside of the new definition. This way, we can make sure that the output of the Employee __str__method will automatically change, if the __str__ method from the superclass Person changes. We could for example add a new attribute age in Person:
class Person: def __init__(self, first, last, age): self.firstname = first self.lastname = last self.age = age def __str__(self): return self.firstname + " " + self.lastname + ", " + str(self.age) class Employee(Person): def __init__(self, first, last, age, staffnum): super().__init__(first, last, age) self.staffnumber = staffnum def __str__(self): return super().__str__() + ", " + self.staffnumber x = Person("Jon", "Snow", 24) y = Employee("Cersei", "Lannister", 42, "266") print(x) print(y)
We have overridden the method __str__ from Person in Employee. By the way, we have overridden __init__ also. Method overriding is an object-oriented programming feature that allows a subclass to provide a different implementation of a method that is already defined by its superclass or by one of its superclasses. The implementation in the subclass overrides the implementation of the superclass by providing a method with the same name, same parameters or signature, and same return type as the method of the parent class.
Overwriting is a term wrongly used for overriding!
In the context of object-oriented programming, you might have heard about “overloading” as well. Overloading is the ability to define the same method, with the same name but with a different number of arguments and types. It’s the ability of one function to perform different tasks, depending on the number of parameters or the types of the parameters.
Let’s look first at the case, in which we have the same number of parameters but different types for the parameters. When we define a function in Python, we don’t have to and we can’t declare the types of the parameters. So if we define the function “successor” in the following example, we implicitly define a family of function, i.e. a function, which can work on integer values, one which can cope with float values and so. Of course, there are types which will lead to an error if used:
>>> def successor(number): ... return number + 1 ... >>> successor(4) 5 >>> successor(3.5) 4.5 >>> successor([1,2,3]) Traceback (most recent call last): File "", line 1, in File "", line 2, in successor TypeError: can only concatenate list (not "int") to list >>>
Having a function with a different number of parameters is another way of function overloading, however this does not work in Python as the error stated in the example above.
If we need such a behaviour, we can simulate it with default parameters:
def f(n, m=None): if m: return n + m +42 else: return n + 42
The * operator can be used as a more general approach for a family of functions with 1, 2, 3, or even more parameters:
def f(*x): if len(x) == 1: return x + 42 else: return x + x + 42
Multiple Inheritance in Python
Multiple Inheritance is inheriting the property of multiple classes into one. In case there are two classes, say A and B, and one needs to create a new class which can inherit the properties of both A and B
class A: # properties of class A class B: # properties of class B class C(A, B): # class C inheriting property of both class A and B # add more properties to class C
As you see, instead of mentioning one class, we simply mentioned another class inside the brackets. And FYI, you can add as many classes you like inside it. Thus, it should actually be like –
class A (A1, A2, A3, ...): # class A inheriting the properties of A1, A2, A3, etc. # Add more properties which A have on its own.
Multilevel Inheritance in Python
In multilevel inheritance, you inherit the classes at multi-levels. We have three classes A, B and C, where A is the super class, B is its sub class and C is the sub class of B.
class A: # properties of class A class B(A): # class B inheriting property of class A # add more properties of class B class C(B): # class C inheriting property of class B # thus, class C also inheriting properties of class A # add more properties of class C