Abstract Class: An abstract class, defined using the abc module (Abstract Base Classes), acts as a blueprint for other classes. It can contain both abstract methods (methods declared but not implemented) and concrete methods (methods with full implementations). Abstract classes cannot be instantiated directly; subclasses must implement all abstract methods to be instantiable. Example of an Abstract Class: Python
Interface vs Abstract Class
This note explains the practical differences between abstract classes and interfaces in Python, provides runnable examples, introduces Protocol as a lightweight interface alternative, and gives interview-style questions with answers.
Quick summary
- Abstract class: a class (usually inheriting from
abc.ABC) that can define abstract methods and concrete methods. It acts as a blueprint and can provide shared implementation. - Interface: not a distinct language construct in Python, but commonly implemented as an all-abstract class using
abc.ABCor viatyping.Protocol(PEP 544) to express structural typing. Interfaces define a contract (a set of methods) that implementing classes must provide.
When to use which: - Use an abstract class when you want a shared base implementation and to enforce a common API. - Use an interface / Protocol when you want to specify behavior (a "can-do" relationship) without prescribing implementation or inheritance.
Abstract class (with examples)
Use the abc module. Abstract classes can contain some implemented (concrete) methods and some abstract methods.
Example (Vehicle):
from abc import ABC, abstractmethod
class Vehicle(ABC):
@abstractmethod
def start_engine(self):
"""Start engine — must be implemented by subclasses."""
pass
def stop_engine(self):
"""A concrete method shared by all vehicles."""
print("Engine stopped.")
class Car(Vehicle):
def start_engine(self):
print("Car engine started with a key.")
class Motorcycle(Vehicle):
def start_engine(self):
print("Motorcycle engine started with a kickstarter.")
if __name__ == '__main__':
car = Car()
car.start_engine()
car.stop_engine()
motorcycle = Motorcycle()
motorcycle.start_engine()
motorcycle.stop_engine()
# vehicle = Vehicle() # TypeError: Can't instantiate abstract class Vehicle
Notes and advanced tips:
- You can provide default behavior in concrete methods on an abstract class to reduce code duplication.
- You can define @abstractmethod on @classmethod and @staticmethod too.
- Use ABC.register(SomeClass) to register a class as a virtual subclass of an ABC without inheritance.
Interface via ABC (all-abstract base class)
An interface is commonly represented by an ABC where every method is abstract. It expresses a strict contract.
Example (Flyable):
from abc import ABC, abstractmethod
class Flyable(ABC):
@abstractmethod
def take_off(self):
pass
@abstractmethod
def land(self):
pass
class Airplane(Flyable):
def take_off(self):
print("Airplane taking off from runway.")
def land(self):
print("Airplane landing on runway.")
class Drone(Flyable):
def take_off(self):
print("Drone taking off vertically.")
def land(self):
print("Drone landing vertically.")
if __name__ == '__main__':
airplane = Airplane()
airplane.take_off()
airplane.land()
drone = Drone()
drone.take_off()
drone.land()
Because Python supports multiple inheritance you can implement multiple ABC-based interfaces by inheriting from several ABCs.
Structural interfaces: typing.Protocol
Protocol (from typing) provides structural typing — duck-typing enforced by static checkers like mypy rather than runtime ABC checks. It is often a more flexible, lighter-weight way to express "interfaces".
Example using Protocol:
from typing import Protocol
class FlyableProto(Protocol):
def take_off(self) -> None: ...
def land(self) -> None: ...
class Bird:
def take_off(self):
print("Bird flaps wings and takes off")
def land(self):
print("Bird lands")
def launch(flyable: FlyableProto):
flyable.take_off()
if __name__ == '__main__':
b = Bird()
launch(b) # Works fine — Bird matches the Protocol shape
Use Protocol when you want interface-like behavior without forcing inheritance.
Key differences (concise)
- Default implementation: Abstract classes may provide concrete methods; interfaces (pure ABC or Protocol) typically don't.
- Inheritance vs structural typing: ABCs are nominal (require explicit inheritance or registration).
Protocolis structural (duck-typed). - Multiple contracts: With ABCs you can implement multiple interfaces via multiple inheritance. Protocols let any object match if it has the required methods.
- Runtime checks:
isinstance(obj, ABC)works for ABCs (including registered virtual subclasses).Protocolis primarily for static checking; runtime isinstance checks are not typical.
Common pitfalls and best practices
- Don't use abstract classes just to hold constants; use modules or dataclasses for that.
- Prefer
Protocolfor light-weight interfaces when you don't need runtime registration or enforcement. - If many classes share implementation, put shared behavior into an abstract base class to avoid duplication.
- Avoid deep inheritance trees; prefer composition when it leads to clearer designs.
Interview questions (with answers)
Basic
1) Q: What is the main difference between an abstract class and an interface in Python?
A: An abstract class (using abc.ABC) can contain both abstract and concrete methods and is nominal (requires inheritance). An interface (when represented by an all-abstract ABC) defines only abstract methods; Protocol provides a structural-interface alternative (duck-typing) preferred for flexibility.
2) Q: Can you instantiate an abstract class? Why or why not?
A: No — abstract classes that contain unimplemented @abstractmethods cannot be instantiated. Python raises TypeError to prevent creating incomplete objects that don't fulfill required behavior.
3) Q: When would you prefer a Protocol over an ABC?
A: Use Protocol when you want structural typing (duck-typing), no forced inheritance, and want static checks from tools like mypy. Use ABC when you need runtime enforcement or to provide shared implementation.
Intermediate
4) Q: Show a small code example where an abstract class provides a default implementation and a subclass overrides it.
A:
from abc import ABC, abstractmethod
class Writer(ABC):
@abstractmethod
def write(self, data: str):
pass
def write_line(self, data: str):
self.write(data + "\n")
class ConsoleWriter(Writer):
def write(self, data: str):
print(data, end='')
cw = ConsoleWriter()
cw.write_line('Hello')
5) Q: How can you register a class as a virtual subclass of an abstract base class?
A: Use MyABC.register(SomeClass). This marks SomeClass as a virtual subclass for isinstance/issubclass checks without changing its inheritance chain.
Advanced
6) Q: Explain trade-offs between using multiple inheritance of ABCs vs composition with interfaces.
A: Multiple inheritance lets a class implement multiple contracts and reuse code from multiple bases, but it can cause method resolution complexity (MRO) and tight coupling. Composition favors single-responsibility and clear dependencies but requires explicit delegation; it's often easier to reason about, test, and maintain.
7) Q: How would you design a plugin system where plugins can optionally implement a configure() hook? Which approach would you choose and why?
A: Use an ABC where configure is optional (provide a concrete no-op default in the base class) or define a Plugin Protocol with an optional configure method (using typing_extensions @runtime_checkable and Protocol for runtime checks if needed). If runtime discovery and registration are required, ABCs with register and entry_points work well. If maximum flexibility and light coupling are important, Protocol is better.
8) Q: Show how to declare and check an abstract class method (@classmethod) as abstract.
A:
from abc import ABC, abstractmethod
class Base(ABC):
@classmethod
@abstractmethod
def create(cls, *args, **kwargs):
pass
class Impl(Base):
@classmethod
def create(cls, name):
return cls()
# Impl.create('x') -> works