Back to Course

Intermediate Python

0% Complete
0/0 Steps
Lesson 21 of 33
In Progress

Abstract Base Classes and Interfaces

In object-oriented programming, it is often useful to define a set of methods that a class should implement, without specifying how those methods should be implemented. This is known as an interface, and it allows you to create a contract that defines the expected behavior of a class. In Python, you can use abstract base classes (ABCs) to define interfaces and enforce the implementation of certain methods.

Abstract Base Classes:

An abstract base class is a class that defines a set of methods that a concrete (i.e., non-abstract) class must implement. In Python, you can define an abstract base class using the abc module and the @abstractmethod decorator.

For example, consider the following abstract base class Shape that defines an area method:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

To create a concrete class that implements the Shape abstract base class, you must provide an implementation for the area method.

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

If you try to create an instance of the Shape abstract base class, you will get an TypeError because it is an abstract class and cannot be instantiated.

s = Shape()  # TypeError: Can't instantiate abstract class Shape with abstract methods area

Instead, you should create an instance of a concrete class that implements the Shape abstract base class, such as Rectangle.

r = Rectangle(5, 7)
print(r.area())  # Output: 35

You can also define multiple abstract methods in an abstract base class. For example:

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

If a concrete class does not implement all of the abstract methods defined in the abstract base class, you will get a TypeError when you try to create an instance of that class.

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

r = Rectangle(5, 7)  # TypeError: Can't instantiate abstract class Rectangle with abstract methods perimeter

Interfaces:

An interface is a set of methods that a class should implement, without specifying how those methods should be implemented. In Python, you can use abstract base classes to define interfaces, as described above.

To create a concrete class that implements the Shape interface, you must provide an implementation for the area method.

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

You can also define multiple methods in an interface. For example:

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

If a concrete class does not implement all of the methods defined in the interface, you will get a TypeError when you try to create an instance of that class.

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

r = Rectangle(5, 7)  # TypeError: Can't instantiate abstract class Rectangle with abstract methods perimeter

You can also define multiple interfaces in a single abstract base class, using separate subclasses for each interface. For example:

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Polygon(Shape):
    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Polygon):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

In this example, the Rectangle class implements both the Shape interface (by providing an implementation for the area method) and the Polygon interface (by providing an implementation for the perimeter method).

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Write an abstract base class Shape that defines an area method, and a concrete class Rectangle that implements the Shape abstract base class. Test your implementation by creating a Rectangle object and printing its area.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

r = Rectangle(5, 7)
print(r.area())  # Output: 35

Write an abstract base class Shape that defines an area method and a perimeter method, and a concrete class Rectangle that implements the Shape abstract base class. Test your implementation by creating a Rectangle object and printing its area and perimeter.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

r = Rectangle(5, 7)
print(r.area())  # Output: 35
print(r.perimeter())  # Output: 24

Write an abstract base class Shape that defines an area method and a perimeter method, and a concrete class Circle that implements the Shape abstract base class. Test your implementation by creating a Circle object with radius 5 and printing its area and perimeter.

from abc import ABC, abstractmethod
import math

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return math.pi * self.radius**2

    def perimeter(self):
        return 2 * math.pi * self.radius

c = Circle(5)
print(c.area())  # Output: 78.53981633974483
print(c.perimeter())  # Output: 31.41592653589793

Write an abstract base class Shape that defines an area method, a perimeter method, and a volume method. Create a concrete class Sphere that implements the Shape abstract base class, and test your implementation by creating a Sphere object with radius 5 and printing its area, perimeter, and volume.

from abc import ABC, abstractmethod
import math

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

    @abstractmethod
    def volume(self):
        pass

class Sphere(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 4 * math.pi * self.radius**2

    def perimeter(self):
        return 2 * math.pi * self.radius

    def volume(self):
        return (4/3) * math.pi * self.radius**3

s = Sphere(5)
print(s.area())  # Output: 314.1592653589793
print(s.perimeter())  # Output: 31.41592653589793
print(s.volume())  # Output: 523.5987755982989

Write an abstract base class Shape that defines an area method, a perimeter method, and a volume method. Create a concrete class Cylinder that implements the Shape abstract base class, and test your implementation by creating a Cylinder object with radius 5 and height 7 and printing its area, perimeter, and volume.

from abc import ABC, abstractmethod
import math

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

    @abstractmethod
    def volume(self):
        pass

class Cylinder(Shape):
    def __init__(self, radius, height):
        self.radius = radius
        self.height = height

    def area(self):
        return 2 * math.pi * self.radius * self.height + 2 * math.pi * self.radius**2

    def perimeter(self):
        return 2 * math.pi * self.radius

    def volume(self):
        return math.pi * self.radius**2 * self.height

c = Cylinder(5, 7)
print(c.area())  # Output: 471.2388980384689
print(c.perimeter())  # Output: 31.41592653589793
print(c.volume())  # Output: 523.5987755982989