OOP
OOP in Python looks familiar — class, inheritance, methods — yet almost every mechanism underneath differs from C++ or Java, and interviews aim at exactly that gap. There is no privacy: there is the _x convention and the mangling of __x into _Class__x. There is no interface as a separate entity: there is duck typing, abc.ABC for nominal checks and typing.Protocol for structural ones. There is no overloading by signature: a class body is an ordinary namespace, so a second definition simply overwrites the first. And instance attributes live in a plain dict, so you can bolt anything onto an object at runtime — until the class declares __slots__.
The second peculiarity is that object behaviour is defined by protocols, not by inheriting a base type. Length comes from __len__, comparison from __eq__, slicing from __getitem__, the with block from the __enter__/__exit__ pair. The interpreter looks these methods up on the type, never on the instance, and wires them to syntax. The third part of the topic grows from there: SOLID and the GoF patterns still hold in Python, but half of them collapse — a first-class function replaces a strategy class, a module replaces a singleton, functools.singledispatch replaces a visitor. Being able to say where a pattern is needed and where it is redundant in a language with first-class functions is what separates a middle engineer from someone who memorised the catalogue.
Topic map
- The four pillars of OOP — encapsulation, inheritance, polymorphism, abstraction, and what each turns into in Python.
- Abstraction — extracting a contract without implementation detail, and how it differs from encapsulation.
- Encapsulation — the
_x/__xconventions,propertyinstead of getters, and why there is no data hiding. - Inheritance and super() — overriding methods, the forms of
super(), and the cost of naming the base class directly. - Polymorphism — overriding, operator overloading,
singledispatch, and why there is no overloading by signature. - Duck typing — checking behaviour instead of type, EAFP, and
Protocolversusabc.ABC. - MRO and C3 linearization — the method resolution order, the C3 rules, and why some hierarchies refuse to compile.
- Multiple inheritance — the diamond, cooperative
super(), and passing arguments down the chain. - Mixins — a small stateless behaviour class, and why base order is critical.
- The instance dict and __slots__ — where attributes live, class-attribute shadowing, and what
__slots__buys. - Magic methods — protocols instead of interfaces, dunder lookup on the type,
__new__versus__init__. - Equality and identity —
isversus==, the__eq__/__hash__contract, andNotImplemented. - Name mangling —
__xbecomes_Class__x; it is collision protection, not privacy. - Context managers —
__enter__/__exit__, suppressing an exception by returningTrue, andcontextlib. - Abstract base classes —
abc.ABC,@abstractmethod, the instantiation ban, andProtocolas the alternative. - SOLID — the five principles and how they read in a duck-typed language.
- Cohesion and coupling — cohesion inside a module, coupling between modules, and why they get confused.
- Composition over inheritance — delegation versus a hierarchy, and the trap of subclassing
list. - DRY, KISS, YAGNI, SLAP — four everyday principles and how each is over-applied.
- Design patterns — the three GoF categories, why a shared vocabulary matters, and when a pattern is noise.
- Creational patterns — factory method, abstract factory, builder, prototype, and their Pythonic replacements.
- Structural patterns — adapter, decorator, facade, proxy, composite, bridge, flyweight.
- Behavioral patterns — strategy, observer, command, template method, state, chain of responsibility and the rest.
- Singleton — via
__new__, via a metaclass, and why a module is usually enough in Python.
Common mistakes and traps
| Mistake | Consequence |
|---|---|
Calling __x a private field | It is only a rename to _Class__x — the attribute is reachable from outside; the protection is against collisions in a hierarchy |
| A mutable class attribute where an instance attribute was meant | The list or dict is shared by every instance; self.items.append(x) shows up in all objects at once |
Forgetting super().__init__() in an overridden __init__ | The base class is never initialised, its attributes are missing, and the first access raises AttributeError |
Defining __eq__ without __hash__ | Python sets __hash__ = None, the object becomes unhashable and cannot go into a set or a dict key |
Believing super() calls the parent | super() walks the MRO of the instance's type — in a diamond it lands in a sibling branch, not in the base class |
Returning a truthy value from __exit__ by accident | The exception is silently suppressed, the error vanishes from the logs, and the with block looks successful |
Subclassing list/dict to "add behaviour" | Built-in methods call each other inside C code, bypassing your overrides — some operations go unnoticed |
| Porting the GoF catalogue into Python literally | Strategy, command and visitor become redundant classes where a function, a partial or singledispatch suffices |
What interviews check
OOP is asked at every level, at different depths. A junior must name the four pillars, explain super(), say where attributes are stored, and write a class with __init__ and __repr__. A middle gets MRO and diamond questions ("what does this print"), __slots__, __eq__ together with __hash__, a hand-written context manager, and the difference between abc.ABC and Protocol. Design patterns form their own block: you are asked not for a definition but for the problem the pattern solves, almost always followed by "and how is it done in Python". Here "with a function" is more often the right answer than "with a class".
The topic is failed in three ways. First, reciting Java OOP: "private fields", "interfaces", "method overloading". None of the three exists in Python in that form, and the interviewer will probe it immediately with __x or with two defs of the same name. Second, definitions instead of mechanism: "polymorphism is when objects of different classes behave differently", without a word about method lookup along the MRO or about dunder protocols. Third, patterns for their own sake: a hand-rolled Singleton metaclass where a module is already a singleton, or a five-class strategy hierarchy where a dict of functions would do. Prepare so that every term comes with a ten-line snippet and one pitfall you name out loud.