Python Abstraction

In the last lesson you learned Encapsulation, which is about keeping data safe inside an object. Abstraction is the next idea right after it. Here we care about hiding the messy inner steps. So the person using your class only sees the simple part they actually need.

πŸ€” Why Abstraction?

Think about driving a car. You turn the wheel, press the pedals, and the car moves. You do not think about fuel injection, pistons, or spark timing. All of that complicated work is hidden. You are given a simple set of controls instead.

Code has the same problem. So let me put it plainly:

  • A class can hold a lot of complicated logic inside it.
  • If everyone who uses that class has to understand every internal step, the class becomes painful to use.
  • Abstraction fixes this by showing a simple interface on the outside and hiding the complicated details on the inside.

So abstraction means this:

  • Show the user only what the class does.
  • Hide how it actually does it.
  • Let the inside change later without breaking the people who use it.

🧩 A Simple Mental Model

When you call len("hello") in Python, you get back 5. You did not write the loop that counts the characters. Python hides that work and gives you one clean function to call.

That is abstraction in everyday use. The hard part is done somewhere inside. You just call the simple thing on the surface.

Tip

A good rule: if a detail does not change how someone uses your class, hide it. Show the simple controls, hide the inside steps.

πŸ” Abstraction vs Encapsulation

These two get mixed up a lot, so let me make the line between them clear. They are close cousins, but they solve different problems.

  • Encapsulation hides data. It wraps the variables inside an object and controls who can touch them, so nobody changes your values in a wrong way.
  • Abstraction hides complexity. It wraps the messy steps behind a simple method name, so the caller does not need to know how the work gets done.

So encapsulation is about protecting the what is stored. Abstraction is about hiding the how it works. You often use both together, but they are answering different questions.

πŸ› οΈ Abstraction with the abc Module

Python gives us a clean way to set up abstraction with the abc module. The name abc stands for Abstract Base Class. An abstract base class is a class that is meant to be a blueprint. You never create an object from it directly. Instead, other classes build on top of it.

The key tool is @abstractmethod. When you mark a method with @abstractmethod, you are saying this: every child class must provide its own version of this method. If a child forgets, Python refuses to create the object.

Here is the basic shape of it.

from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass

Let’s walk through what each part does:

  • from abc import ABC, abstractmethod brings in the two tools we need.
  • class Shape(ABC) makes Shape an abstract base class by inheriting from ABC.
  • @abstractmethod sits above area and marks it as a method every child must fill in.
  • pass is just an empty body. The base class only describes what must exist, not how it works.

The why is simple. Shape is the blueprint. It promises that anything calling itself a shape will have an area method. But it leaves the actual formula to each real shape.

✍️ A Full Example: Shapes

Now let’s give that blueprint some real children. A circle and a rectangle both have an area. But the formula inside each one is different. From the outside you just want to call area and get a number back.

This code defines the Shape blueprint, then two real shapes that fill in the area method.

from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
c = Circle(5)
r = Rectangle(4, 6)
print(f"Circle area: {c.area()}")
print(f"Rectangle area: {r.area()}")

Running this prints:

Output

Circle area: 78.53975
Rectangle area: 24

See what happened here:

  • Shape is the abstract base class. It says every shape must have an area method, but it does not say how to compute it.
  • Circle and Rectangle each inherit from Shape and write their own area. The circle uses the radius. The rectangle multiplies width and height.
  • From the outside you just call .area() on either one. You do not care about the formula inside.

That is the win. Both objects are used the same way, even though the work inside is totally different.

🀝 Why This Is Useful: A Shared Contract

Here is the real reason abstraction matters. The abstract base class works like a contract. It forces every child to provide the same set of methods. So the rest of your code can trust those methods exist.

Think about an app that takes payments. You might have a card payment, a wallet payment, and a bank transfer. They all work very differently inside. But you want one rule for all of them: every payment method must have a pay method.

This code sets that rule with an abstract base class, then writes two payment types that follow it.

from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def pay(self, amount):
pass
class CardPayment(Payment):
def pay(self, amount):
print(f"Paid {amount} using a debit card.")
class WalletPayment(Payment):
def pay(self, amount):
print(f"Paid {amount} using the app wallet.")
def checkout(method, amount):
method.pay(amount)
checkout(CardPayment(), 500)
checkout(WalletPayment(), 120)

Running this prints:

Output

Paid 500 using a debit card.
Paid 120 using the app wallet.

Now look at the checkout function. It does not check what kind of payment it got. It just calls method.pay(amount) and trusts that pay is there. The abstract base class guarantees that. The same idea works for notification channels. Email, SMS, and push could all share a send method, so your code can call send on any of them without caring which channel it is.

πŸ”— Mixing Abstract and Concrete Methods

One more useful thing. An abstract base class does not have to be only abstract methods. You can mix in normal methods too, the ones with real working bodies. We call those concrete methods.

This code adds a normal describe method to the Shape blueprint, while area stays abstract.

from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
def describe(self):
print(f"This shape has an area of {self.area()}.")
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side * self.side
s = Square(3)
s.describe()

Running this prints:

Output

This shape has an area of 9.

So here:

  • area is abstract. Every child must write it.
  • describe is concrete. It already has a working body, and every child gets it for free.
  • Notice describe even calls self.area(). It does not know the formula, but it trusts the child to provide one.

That is a nice pattern. You force the parts that must differ, and you share the parts that stay the same.

🚫 What Happens If You Skip the Method?

This is the part that makes abstraction strict, in a good way. If a child class does not provide the abstract method, Python stops you from creating an object.

This code defines a child that forgets to write area, then tries to use it.

from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Mystery(Shape):
pass
m = Mystery()

Python refuses and raises an error:

Output

Traceback (most recent call last):
File "main.py", line 10, in <module>
m = Mystery()
TypeError: Can't instantiate abstract class Mystery without an implementation for abstract method 'area'

This error is actually a friend:

  • It catches the mistake early, right when you try to build the object.
  • So you never end up with a half-finished shape floating around in your program.
  • The message even tells you which method you forgot, so the fix is obvious.

The fix is simple. Add the missing method, and the same class works fine.

class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
t = Triangle(10, 4)
print(t.area())

Output

20.0

So the rule is clear. A child must implement every abstract method before Python will let you create it.

⚠️ Common Mistakes

A few slip-ups come up a lot when people first use abstract classes.

  • Trying to create an object straight from the abstract base class. You cannot. It is only a blueprint.
# ❌ Avoid: Shape is abstract, this fails
shape = Shape()
# βœ… Good: create a real child that fills in area
shape = Circle(5)
  • Forgetting to import abstractmethod and only importing ABC. Without the decorator, nothing is forced.
# ❌ Avoid: missing abstractmethod, the rule is not enforced
from abc import ABC
# βœ… Good: bring in both
from abc import ABC, abstractmethod
  • Forgetting to put @abstractmethod above the method. Then it is just a normal empty method, and Python does not force the child to write it.
# ❌ Avoid: no decorator, so this rule is silently skipped
class Shape(ABC):
def area(self):
pass
# βœ… Good: the decorator makes the rule real
class Shape(ABC):
@abstractmethod
def area(self):
pass
  • Marking a method as abstract but giving the child a method with a different name. The child still counts as incomplete, so Python blocks it.

βœ… Best Practices

Keep these habits in mind and abstraction stays easy to work with.

  • Put only the shared promise in the abstract base class. The common method names go here, not the detailed logic.
  • Give the abstract method a clear name that describes the action, like pay, area, or send.
  • Let each child class own its own details. That is the whole point: the inside can differ while the outside stays the same.
  • Move any logic that every child would repeat into a concrete method on the base class. Share what is the same, force what is different.
  • Reach for an abstract base class when several classes should all support the same set of actions but do them in their own way.

🧩 What You’ve Learned

A quick recap of the ideas from this lesson:

  • βœ… Abstraction hides complicated inner details behind a simple interface. It shows what, not how.
  • βœ… The car analogy: you use the wheel and pedals without knowing the engine internals.
  • βœ… Encapsulation hides data; abstraction hides complexity.
  • βœ… The abc module gives you ABC and @abstractmethod to build abstract base classes.
  • βœ… A class that inherits from ABC becomes a blueprint you cannot create objects from directly.
  • βœ… @abstractmethod forces every child class to provide its own version of that method.
  • βœ… You can mix abstract methods with normal concrete methods in the same base class.
  • βœ… If a child skips an abstract method, Python raises a TypeError and refuses to build the object.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does abstraction mean in object-oriented programming?

    Why: Abstraction shows the user a simple interface while hiding the complicated steps inside.

  2. 2

    What does the @abstractmethod decorator do?

    Why: A method marked with @abstractmethod must be filled in by each child class.

  3. 3

    What happens if you try to create an object directly from an abstract base class?

    Why: An abstract base class is only a blueprint, so Python will not instantiate it directly.

  4. 4

    How is abstraction different from encapsulation?

    Why: Encapsulation protects the data inside an object; abstraction hides the messy steps behind a simple interface.

πŸš€ What’s Next?

You now know how to hide details and force a shared set of actions across classes. Next we look at the special methods that let your objects behave like built-in types, such as printing nicely or adding with +.

Magic Methods

Share & Connect