Python

Object Oriented Programming

Abstraction & Abstract Base Classes

A car's steering wheel is an ABSTRACTION:

JrCodex·6 min read

Jr Codex Python Notes

Level: Advanced Prerequisites: Chapter 5 Time to complete: ~25 minutes


Table of Contents

  1. What is Abstraction?
  2. The Informal Approach — And Why It's Not Enough
  3. The abc Module
  4. @abstractmethod in Practice
  5. Abstract Classes Can Still Share Code
  6. Abstract Properties
  7. Abstraction vs Encapsulation — Don't Confuse Them
  8. Summary & Next Steps

1. What is Abstraction?

Abstraction means defining what a class must do, without necessarily saying how — hiding implementation complexity behind a simple, guaranteed interface. Chapter 3's Shape class was already an early, informal example of this idea.

Abstraction in Everyday Terms
─────────────────────────────────────────
  A car's steering wheel is an ABSTRACTION:
    You know turning it left turns the car left.
    You do NOT need to know the steering rack, tie rods,
    or power steering pump mechanics underneath.
─────────────────────────────────────────

In OOP, abstraction usually means: define a base class that specifies required methods (the contract), and guarantee that every subclass implements them — without providing (or caring about) the actual implementation in the base class itself.


2. The Informal Approach — And Why It's Not Enough

Chapter 3 used this pattern:

class Shape:
    def area(self):
        raise NotImplementedError("Subclasses must implement area()")
 
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    # oops — forgot to implement area()!
 
rect = Rectangle(4, 5)
print(rect.area())     # NotImplementedError — but only discovered at RUNTIME,
                          # only if this exact line ever gets called

The problem: nothing stops you from instantiating an incomplete subclass, or from forgetting to implement area() entirely — the mistake only surfaces the moment someone happens to call the missing method, which could be much later, in production. Python's abc module fixes this by enforcing the contract at class-definition time.


3. The abc Module

ABC (Abstract Base Class) and @abstractmethod, from Python's built-in abc module, turn "please implement this" into "you literally cannot create this object until you do."

from abc import ABC, abstractmethod
 
class Shape(ABC):                     # inherits from ABC — marks this as abstract
    @abstractmethod
    def area(self):
        """Every subclass MUST implement this."""
        pass
 
    @abstractmethod
    def perimeter(self):
        pass
 
shape = Shape()      # TypeError: Can't instantiate abstract class Shape
                        # with abstract methods area, perimeter

You cannot create a Shape() directly at all — Python refuses, right at the point of instantiation, long before any specific method gets called.


4. @abstractmethod in Practice

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
    # missing perimeter() on purpose, to show what happens
 
 
rect = Rectangle(4, 5)
# TypeError: Can't instantiate abstract class Rectangle
# with abstract method perimeter
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):                # now BOTH abstract methods are implemented
        return 2 * (self.width + self.height)
 
 
rect = Rectangle(4, 5)      # ✓ works now — the contract is fully satisfied
print(rect.area())              # 20
print(rect.perimeter())            # 18

The key guarantee abc provides: if a subclass is missing any @abstractmethod, Python refuses to let you create an instance of it — you find out immediately, at the exact moment you try, not buried deep in a later runtime call.


5. Abstract Classes Can Still Share Code

An abstract base class isn't limited to empty method stubs — it can define regular, fully-working methods too. Subclasses only must implement the ones marked @abstractmethod; everything else is inherited normally (Chapter 2).

from abc import ABC, abstractmethod
 
class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
 
    def describe(self):
        """A CONCRETE method — shared by every subclass automatically, no override required."""
        return f"{type(self).__name__} has an area of {self.area():.2f}"
 
 
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
 
    def area(self):
        return 3.14159 * self.radius ** 2
 
 
circle = Circle(5)
print(circle.describe())     # "Circle has an area of 78.54" — inherited, uses area() polymorphically

6. Abstract Properties

@property (Chapter 5) and @abstractmethod combine to enforce that a subclass must provide a specific property, not just a method:

from abc import ABC, abstractmethod
 
class Employee(ABC):
    @property
    @abstractmethod
    def salary(self):
        """Every subclass must define how salary is computed."""
        pass
 
 
class SalariedEmployee(Employee):
    def __init__(self, annual_salary):
        self.annual_salary = annual_salary
 
    @property
    def salary(self):
        return self.annual_salary / 12
 
 
class HourlyEmployee(Employee):
    def __init__(self, hourly_rate, hours_worked):
        self.hourly_rate = hourly_rate
        self.hours_worked = hours_worked
 
    @property
    def salary(self):
        return self.hourly_rate * self.hours_worked
 
 
emp1 = SalariedEmployee(60000)
emp2 = HourlyEmployee(25, 160)
 
print(emp1.salary)     # 5000.0
print(emp2.salary)       # 4000

7. Abstraction vs Encapsulation — Don't Confuse Them

These two terms get mixed up constantly — they solve different problems:

Encapsulation (Chapter 5)Abstraction (this chapter)
Question it answers"How do I protect an object's internal data from invalid changes?""What must a class guarantee it can do, regardless of how?"
Mechanism_protected, __private, @propertyABC, @abstractmethod
DirectionLooking inward — hiding an object's own stateLooking outward — defining a contract other code can rely on
Both Working Together
─────────────────────────────────────────
  class Shape(ABC):                    ← ABSTRACTION: guarantees every
      @abstractmethod                     Shape has an .area() method
      def area(self): pass

  class Circle(Shape):
      def __init__(self, radius):
          self.__radius = radius       ← ENCAPSULATION: protects radius
                                           from becoming an invalid value

      @property
      def radius(self):
          return self.__radius

      @radius.setter
      def radius(self, value):
          if value <= 0:
              raise ValueError("Radius must be positive")
          self.__radius = value

      def area(self):
          return 3.14159 * self.__radius ** 2
─────────────────────────────────────────

8. Summary & Next Steps

Key Takeaways

  • Abstraction defines a required contract (which methods/properties a class must have) without dictating implementation — Python enforces this via the abc module.
  • class Shape(ABC): plus @abstractmethod prevents instantiating any subclass that hasn't implemented every required method — caught immediately, not at some later runtime call.
  • An abstract base class can still provide fully working, shared (concrete) methods alongside its abstract ones.
  • Abstraction (the outward contract) and encapsulation (inward data protection) are different concerns that often combine in the same class.

Concept Check

  1. What error do you get if you try to instantiate a subclass that's missing an @abstractmethod implementation, and when does that error occur?
  2. Can an abstract base class define a fully-implemented, non-abstract method? Give an example.
  3. In one sentence, how does abstraction differ from encapsulation?

Next Chapter

Chapter 7: Dunder Methods & Operator Overloading


Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index