Python Abstraction
Table of Contents + β
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): passLetβs walk through what each part does:
from abc import ABC, abstractmethodbrings in the two tools we need.class Shape(ABC)makesShapean abstract base class by inheriting fromABC.@abstractmethodsits aboveareaand marks it as a method every child must fill in.passis 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.53975Rectangle area: 24See what happened here:
Shapeis the abstract base class. It says every shape must have anareamethod, but it does not say how to compute it.CircleandRectangleeach inherit fromShapeand write their ownarea. 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:
areais abstract. Every child must write it.describeis concrete. It already has a working body, and every child gets it for free.- Notice
describeeven callsself.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.0So 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 failsshape = Shape()
# β
Good: create a real child that fills in areashape = Circle(5)- Forgetting to import
abstractmethodand only importingABC. Without the decorator, nothing is forced.
# β Avoid: missing abstractmethod, the rule is not enforcedfrom abc import ABC
# β
Good: bring in bothfrom abc import ABC, abstractmethod- Forgetting to put
@abstractmethodabove 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 skippedclass Shape(ABC): def area(self): pass
# β
Good: the decorator makes the rule realclass 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, orsend. - 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
abcmodule gives youABCand@abstractmethodto build abstract base classes. - β
A class that inherits from
ABCbecomes a blueprint you cannot create objects from directly. - β
@abstractmethodforces 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
TypeErrorand refuses to build the object.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does abstraction mean in object-oriented programming?
Why: Abstraction shows the user a simple interface while hiding the complicated steps inside.
- 2
What does the @abstractmethod decorator do?
Why: A method marked with @abstractmethod must be filled in by each child class.
- 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
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 +.