Python Abstract Classes
What is an Abstract Class?
An abstract class is a class that: - Is never instantiated directly — no instances of it should ever be created - Serves purely as a base / parent class for other classes - Provides common implementation shared across all child classes - Defines abstract methods that child classes must implement
⚠️ Python has no built-in
abstractkeyword. By convention, prefix the class name withAbstractto signal it should never be instantiated.
Abstract Methods
An abstract method is a method defined in the abstract class that has no real implementation — it raises NotImplementedError to force child classes to override it:
def play(self):
raise NotImplementedError("You must provide an implementation for play")
- If a child class inherits from the abstract class but does not override this method, calling it raises a
NotImplementedError - This acts as a contract: "any class inheriting from me must implement these methods"
Abstract Class vs Concrete Class
| Abstract Class | Concrete Class | |
|---|---|---|
| Instantiated? | ❌ Never | ✅ Yes |
| Has abstract methods? | ✅ Yes (raises NotImplementedError) |
❌ No — all methods implemented |
| Purpose | Define a template / framework | Actual usable implementation |
| Example | AbstractGame |
RandomGuesser |
Full Example — AbstractGame
import random
class AbstractGame:
def start(self):
while True:
start = input("Would you like to play? ").lower()
if start == "yes":
break
self.play()
def end(self):
print("The game has ended.")
self.reset()
# --- Abstract methods (must be implemented by child classes) ---
def play(self):
raise NotImplementedError("You must provide an implementation for play")
def reset(self):
raise NotImplementedError("You must provide an implementation for reset")
startandendare concrete — fully implemented and shared by all gamesplayandresetare abstract — each game must define its own versionstartcallsself.play()andendcallsself.reset()— these only work once a child class implements them
Implementing a Concrete Child Class
class RandomGuesser(AbstractGame):
def __init__(self, rounds):
self.rounds = rounds
self.round = 0
def reset(self):
self.round = 0
def play(self):
while self.round < self.rounds:
self.round += 1
print(f"Welcome to round {self.round}! Let's begin.")
random_num = random.randint(0, 10)
while True:
guess = input("Enter a number between 1 and 10: ")
if int(guess) == random_num:
print("You got it!")
break
self.end()
game = RandomGuesser(2)
game.start() # asks "Would you like to play?", then runs play(), then end()/reset()
What happens step by step:
1. game.start() — prompts user, then calls self.play()
2. play() — runs the game logic for the set number of rounds, then calls self.end()
3. end() — prints "The game has ended", then calls self.reset()
4. reset() — resets self.round = 0 so the game can be replayed
What Happens Without Implementing Abstract Methods
If a child class inherits but doesn't implement play or reset:
class AnotherGame(AbstractGame):
pass # no play or reset defined
game = AnotherGame()
game.start() # user says yes → calls self.play() → 💥 NotImplementedError
NotImplementedError: You must provide an implementation for play
Real-World Use Case — Playing Multiple Games
Because all games inherit from AbstractGame, you can treat them uniformly:
games = [RandomGuesser(2), AnotherGame()]
for game in games:
game.start() # works for every game — start() is guaranteed to exist
This is the main power of abstract classes: a common interface across many different implementations.
Key Design Principles
Concrete methods (like start and end) belong in the abstract class when:
- The behaviour is identical across all child classes
- They rely on abstract methods to do their specific work
Abstract methods belong in the abstract class when: - The behaviour is different for every child class - They represent something the abstract class can't know how to do on its own
Key Takeaways & Recap
| Concept | Summary |
|---|---|
| Abstract class | Never instantiated; used only as a base class |
| Abstract method | Raises NotImplementedError; must be overridden by child classes |
| Concrete method | Fully implemented in the abstract class; shared by all children |
| Naming convention | Prefix with Abstract (e.g. AbstractGame) to signal intent |
NotImplementedError |
Python's way of enforcing that a method must be overridden |
| Purpose | Provide a framework; enforce a contract; avoid duplicate code |