Inheritance in Python
Inheritance is one of the core principles of Object-Oriented Programming (OOP), and it allows one class (the child or subclass) to inherit the properties and behaviours (attributes and methods) of another class (the parent or superclass). This promotes code reuse and improves the maintainability and extensibility of software.
1. Basic Concept of Inheritance
When a subclass inherits from a superclass:
- The subclass automatically gains all the attributes and methods from the superclass.
- The subclass can extend or override the functionality of the superclass.
Example:
# Parent class (Base class)
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "Some generic sound"
# Child class (Derived class)
class Dog(Animal):
def __init__(self, name, breed):
# Call the parent class constructor using super()
super().__init__(name)
self.breed = breed
def speak(self):
return f"{self.name} barks."
# Creating objects
dog = Dog("Buddy", "Golden Retriever")
print(dog.speak()) # Output: Buddy barks.
2. The super()
Function
super()
is used to call the methods of the parent class from the child class. It is particularly useful in the constructor method (__init__
) when you need to initialize the parent class.
Example:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "Some generic sound"
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # Calls the __init__ method of the parent class
self.breed = breed
def speak(self):
return f"{self.name} barks"
# Create an instance of Dog
dog = Dog("Charlie", "Labrador")
print(dog.speak()) # Output: Charlie barks
In the example above:
- The
Dog
class inherits from theAnimal
class. - The
Dog
class usessuper().__init__(name)
to initialize thename
attribute inherited from theAnimal
class.
3. Method Overriding in Inheritance
Method overriding occurs when a subclass defines a method with the same name as a method in the parent class, but with a different implementation.
This allows the subclass to provide its own specific functionality for methods inherited from the superclass.
class Animal:
def speak(self):
return "Animal makes a sound."
class Dog(Animal):
def speak(self): # Overriding the speak method of the parent class
return "Dog barks."
# Creating an object of Dog
dog = Dog()
print(dog.speak()) # Output: Dog barks.
In this example:
- The
Dog
class overrides thespeak
method of theAnimal
class to provide its own implementation.
4. Inheritance with Multiple Classes (Multiple Inheritance)
In Python, a class can inherit from multiple classes, which is called multiple inheritance. This allows a class to inherit attributes and methods from more than one parent class.
class Animal:
def speak(self):
return "Some sound"
class Mammal:
def walk(self):
return "Walking on land"
class Dog(Animal, Mammal): # Inheriting from both Animal and Mammal
def bark(self):
return "Barking"
dog = Dog()
print(dog.speak()) # Output: Some sound
print(dog.walk()) # Output: Walking on land
print(dog.bark()) # Output: Barking
In this case:
- The
Dog
class inherits from both theAnimal
andMammal
classes. - It has access to methods
speak
fromAnimal
andwalk
fromMammal
.
5. Inheritance and Access Modifiers
In Python, there are no strict access modifiers like in some other languages (e.g., private
, protected
). However, Python uses a naming convention to indicate the intended visibility of attributes and methods:
- Public members: No leading underscores (
attribute
ormethod
). - Protected members: A single leading underscore (
_attribute
). - Private members: A double leading underscore (
__attribute
).
Accessing Private and Protected Attributes
- Private attributes can be accessed directly within the class, but they are not intended to be accessed from outside the class.
- Protected attributes are intended to be accessible from subclasses, but it’s a convention to treat them as private.
class Animal:
def __init__(self, name):
self.__name = name # Private attribute
self._species = "Mammal" # Protected attribute
def get_name(self):
return self.__name # Accessing private attribute via a method
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name)
self.breed = breed
def display_info(self):
return f"{self.get_name()} is a {self.breed}."
# Creating an object of Dog
dog = Dog("Buddy", "Golden Retriever")
print(dog.display_info()) # Output: Buddy is a Golden Retriever
In this example:
- The
__name
attribute is private in theAnimal
class, so it cannot be accessed directly outside the class, but we use theget_name
method to retrieve it. - The
_species
attribute is protected, which means it’s meant to be accessed within the class or its subclasses, but it’s not strictly private.
6. Abstract Classes and Inheritance
An abstract class is a class that cannot be instantiated directly and typically contains abstract methods that must be implemented by subclasses. It provides a template for other classes to inherit from.
- To define an abstract class, you need to import the
abc
module and use theABC
class as a base class, along with the@abstractmethod
decorator to mark methods as abstract.
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
# The following will raise an error since Animal cannot be instantiated directly
# animal = Animal() # Error: Can't instantiate abstract class Animal with abstract methods speak
# Creating an instance of Dog (since it implements the speak method)
dog = Dog()
print(dog.speak()) # Output: Woof!
7. Key Benefits of Inheritance
- Code Reusability: Subclasses can reuse code from the parent class.
- Extensibility: New features can be added to existing classes without modifying the original code.
- Maintainability: Changes to the parent class automatically propagate to subclasses.
- Polymorphism: Subclasses can provide different implementations of methods that have the same name, allowing for flexible behavior.