Python OOP interviews rarely stop at "What is a class?" They ask which method runs in a diamond, why two objects compare equal but cannot go into a set, or what super() actually calls. Each answer follows a language rule, not personal style.
Once those rules are clear, output questions become traces. You inspect the instance, walk the method resolution order, and apply the relevant dunder method.
Classes, instances and self
A class defines data and behaviour. An instance is one object created from that class. In an instance method, self refers to the instance on which the method was called, and Python passes it automatically.
class Student:
school = "KnowledgeGate"
def __init__(self, name):
self.name = name
def introduce(self):
return f"I am {self.name}"Student.school is a class attribute, shared through the class. self.name is an instance attribute, stored separately on each student object.
__init__ is an initialiser. By the time it runs, Python has already created the instance. That is why calling it a constructor, in the strict C++ or Java sense, is imprecise. Object creation is handled by __new__; ordinary application code usually customises __init__.
The classic class-attribute trap uses a mutable value:
class Box:
tags = []
a = Box()
b = Box()
a.tags.append(1)
print(b.tags) # [1]Both instances reach the same list through the class. If every object needs its own list, assign self.tags = [] inside __init__.
Inheritance and what super() follows
Inheritance lets a specialised class reuse and override behaviour from a base class:
class Animal:
def speak(self):
return "sound"
class Dog(Animal):
def speak(self):
return "bark"Dog inherits from Animal, but its own speak() wins because attribute lookup finds it first.
The subtle rule is that super() does not simply mean "call my parent". It means "continue from the current class to the next class in this instance's method resolution order." With single inheritance, those ideas often look identical. Multiple inheritance exposes the difference.
Cooperative classes call super() consistently:
class Named:
def __init__(self, name, **kwargs):
self.name = name
super().__init__(**kwargs)When every class in a compatible hierarchy accepts what it needs and forwards the rest, a diamond can initialise each class once along one ordered path. A class that calls a base by name can skip part of that cooperative chain or initialise a base twice.
Method Resolution Order and the diamond
Consider the classic diamond:
class A:
def who(self):
return "A"
class B(A):
def who(self):
return "B"
class C(A):
def who(self):
return "C"
class D(B, C):
passD().who() returns "B".
Python uses C3 linearisation to build a consistent method resolution order. For this hierarchy:
D.__mro__
(D, B, C, A, object)Python prints each entry as a full class repr, abbreviated above to the bare class names. Attribute lookup walks that tuple from left to right. D has no who. B does, so lookup stops and returns B's method without reaching C or A.
The compact derivation is:
L[D] = D + merge(L[B], L[C], [B, C])
= D, B, C, A, objectThe merge preserves local parent order, so B stays before C because class D(B, C) declared it that way. It also preserves monotonicity, meaning a subclass does not reverse an ordering already established in its parents. That is why MRO is more than a simple depth-first walk.

Print ClassName.__mro__ whenever a multiple-inheritance question feels ambiguous. The tuple is the rule Python will actually follow. The broader OOP concepts guide for CS exams helps connect this Python-specific mechanism to inheritance, polymorphism, abstraction, and encapsulation.
Dunder methods that carry interview weight
Dunder methods, named with double underscores, connect your class to Python's object model.
__repr__should give a useful, unambiguous developer-facing representation.__str__should give a readable user-facing form. If it is absent,str(obj)falls back to__repr__.__len__supportslen(obj).__call__lets an instance be called like a function.__getitem__supports indexed or key-based access.
Equality and hashing are a particularly important pair:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.yNow Point(1, 2) == Point(1, 2) is True. However, defining value equality without a compatible __hash__ makes this class unhashable. hash(Point(1, 2)) raises TypeError, so the object cannot be a set member or dictionary key.
For an effectively immutable point, a compatible implementation is:
def __hash__(self):
return hash((self.x, self.y))Equal points then produce equal hashes because both methods use the same fields. Do not mutate those fields while an object is stored in a set or used as a dictionary key. If points are meant to be mutable, leaving them unhashable is safer.
classmethod, staticmethod and property
These decorators describe how a method binds:
@classmethodreceivesclsand is useful for alternative constructors such asPoint.from_tuple((1, 2)).@staticmethodreceives neitherselfnorcls. It is a related plain function kept in the class namespace.@propertyexposes a method through attribute syntax, which is useful for a computed or validated value.
A property without a setter is read-only through normal assignment. It can compute a value each time while keeping the public interface as simple as obj.value.
Traps interviewers set
Four traps recur because each reveals whether you know the rule underneath:
A mutable class attribute is shared across instances.
Defining
__eq__without a compatible__hash__removes hashability.Skipping
super().__init__()can leave a base class uninitialised.ischecks identity, while==checks value equality. Two empty lists compare equal, but they are distinct objects.
Interviewers also ask you to predict a method from an MRO, distinguish __str__ from __repr__, or write a small value class with correct equality. Our question bank carries over 450 Python practice questions and close to 200 language-independent OOP questions covering these class, inheritance, and dunder patterns. The Python interview questions hub gives the surrounding freshers-level question map.
The short version and next step
self is the instance. super() continues through the MRO, not merely to a class you informally call the parent. In D(B, C), the diamond resolves through D, B, C, A, and object. Defining __eq__ without __hash__ also costs an object set membership.
Build the language foundation in the Python Programming course, then practise the interview layer with DSA Using Python. The Coding and DSA courses section places Python beside C, C++, Java, and the data-structures practice the next interview round tests.




