15  Abstract Base Class

15.1 ABC

Goal:

  • Prevents a user from creating an object of that class
  • compels a user to override abstract methods in a child class

abstract class = a class which contains one or more abstract methods. abstract method = a method that has a declaration but does not have an implementation.

from abc import ABC, abstractmethod
class Vehicle(ABC):

    @abstractmethod
    def go(self):
        pass

    @abstractmethod
    def stop(self):
        pass

class Car(Vehicle):

    def go(self):
        print("You drive the car")

    def stop(self):
        print("This car is stopped")

class Motorcycle(Vehicle):

    def go(self):
        print("You ride the motorcycle")

    def stop(self):
        print("This motorcycle is stopped")
# Cannot Do
# vehicle = Vehicle()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 2
      1 # Cannot Do
----> 2 vehicle = Vehicle()

TypeError: Can't instantiate abstract class Vehicle with abstract methods go, stop
car = Car()
motorcycle = Motorcycle()

car.go()
motorcycle.go()

#vehicle.stop()
car.stop()
motorcycle.stop()
You drive the car
You ride the motorcycle
This car is stopped
This motorcycle is stopped

15.2 ABC with Data Class

from abc import ABC, abstractmethod
from dataclasses import dataclass

@dataclass
class Shape(ABC):
    name: str  # Field automatically added by @dataclass

    @abstractmethod
    def area(self) -> float:
        pass

    @abstractmethod
    def perimeter(self) -> float:
        pass

# Subclass that implements the abstract methods
@dataclass
class Rectangle(Shape):
    width: float
    height: float

    def area(self) -> float:
        return self.width * self.height

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

# Subclass that implements the abstract methods
@dataclass
class Circle(Shape):
    radius: float

    def area(self) -> float:
        return 3.14159 * self.radius ** 2

    def perimeter(self) -> float:
        return 2 * 3.14159 * self.radius
# Now you can instantiate the subclasses
rect = Rectangle(name="My Rectangle", width=10, height=5)
circle = Circle(name="My Circle", radius=7)

print(f"{rect.name} Area: {rect.area()}")        # Output: My Rectangle Area: 50
print(f"{rect.name} Perimeter: {rect.perimeter()}") # Output: My Rectangle Perimeter: 30

print(f"{circle.name} Area: {circle.area()}")      # Output: My Circle Area: 153.93791
print(f"{circle.name} Perimeter: {circle.perimeter()}") # Output: My Circle Perimeter: 43.98226
My Rectangle Area: 50
My Rectangle Perimeter: 30
My Circle Area: 153.93791
My Circle Perimeter: 43.98226