Skip to content
CodeKruPro

Your language · Python

Classes and objects (OOP)

Classes, objects, constructors and the four pillars with real examples.

Learn: Classes and objects in Python: the four pillars9 minute read

Object-oriented programming (OOP) means building a program from objects that hold data together with the code that works on it. Python does OOP with fewer rules than Java: there are no access modifiers (keywords such as private that control who can use a member), no interface keyword (a type that lists methods without bodies) and no overloading (several methods with one name and different parameters), and a class may have several parents. Interviewers ask you to explain the four pillars with Python examples, and they check that you understand self, super() and the method resolution order (explained in step 4).

Step 1: classes, objects, __init__ and self

A class is a blueprint, and calling it creates an object (an instance of the class). Python first runs __new__ to create the object and then __init__ to set it up. self is the object itself; Python passes it as the first parameter of every instance method, and you set attributes on it, as in self.driver = driver. Attributes written in the class body are class attributes, shared by every object, much like Java's static fields.

If a subclass defines its own __init__, the parent's __init__ does not run unless you call super().__init__(...). Forgetting that call is a common cause of AttributeError (an error that means the object has no such attribute).

Step 2: the four pillars, the Python way

  • Encapsulation (keeping data inside an object and exposing only methods) works by convention. A leading underscore (self._driver) marks internal parts, two underscores trigger name mangling (Python renames the attribute to avoid clashes), and @property controls reading and writing.
  • Inheritance: class LoginPage(BasePage) reuses the parent's code. LoginPage is the subclass (child) and BasePage is the parent class.
  • Polymorphism (one call, many behaviours) comes from overriding (a subclass replaces a parent method) and from duck typing: any object that has the right method works, with no common parent needed.
  • Abstraction (showing what an object does and hiding how) comes from clear public methods and from abstract base classes (ABCs) in the abc module.
from abc import ABC, abstractmethod

class BasePage(ABC):
    opened = 0                                  # class attribute, shared

    def __init__(self, name):
        self._name = name                       # internal by convention
        BasePage.opened += 1

    @abstractmethod
    def is_loaded(self): ...

    def describe(self):
        return f"{self._name} loaded={self.is_loaded()}"

class LoginPage(BasePage):
    def __init__(self):
        super().__init__("Login")

    def is_loaded(self):
        return True

class CartPage(BasePage):
    def __init__(self, items):
        super().__init__("Cart")
        self._items = items

    def is_loaded(self):
        return self._items >= 0

    def describe(self):                         # override and extend
        return super().describe() + f" items={self._items}"

for page in [LoginPage(), CartPage(2)]:
    print(page.describe())
print("pages opened:", BasePage.opened)

try:
    BasePage("x")
except TypeError as e:
    print("TypeError:", e)
Login loaded=True
Cart loaded=True items=2
pages opened: 2
TypeError: Can't instantiate abstract class BasePage without an implementation for abstract method 'is_loaded'

An abstract base class cannot be created, and neither can a subclass that leaves an abstract method out. The error comes when you create the object, not when you define the class. For interfaces, Python mostly relies on duck typing, and typing.Protocol can describe the expected methods for type checkers (tools that check types before the code runs).

Step 3: overriding, but no overloading

Any method can be overridden, and super().method() calls the next version up. A class holds one attribute per name, so a second method with the same name replaces the first; there is no overloading by parameter list. Default arguments and *args cover that need. Operators can be overloaded through special methods with double underscores (dunder methods) such as __eq__ (for ==), __add__ (for +) and __str__ (for text shown by print).

Step 4: multiple inheritance and the MRO

When a class has several parents, Python searches them in a fixed order called the method resolution order (MRO). The rule: the class itself first, then its parents from left to right, each class only once, and never a parent before its children. super() means "the next class in the MRO", which solves the diamond problem, because a shared parent's method runs only once.

CheckoutTest inherits from ScreenshotMixin and LoggingMixin, which both inherit from Base; the MRO visits CheckoutTest, ScreenshotMixin, LoggingMixin, Base and object in that order.
A diamond of classes and the order Python searches them.
class Base:
    def setup(self):
        return ["Base"]

class ScreenshotMixin(Base):
    def setup(self):
        return ["Screenshot"] + super().setup()

class LoggingMixin(Base):
    def setup(self):
        return ["Logging"] + super().setup()

class CheckoutTest(ScreenshotMixin, LoggingMixin):
    pass

print(CheckoutTest().setup())
print([c.__name__ for c in CheckoutTest.__mro__])
['Screenshot', 'Logging', 'Base']
['CheckoutTest', 'ScreenshotMixin', 'LoggingMixin', 'Base', 'object']

For classes that mostly hold data, @dataclass (a decorator, a label above a class that adds features to it) writes __init__, __repr__ and __eq__ for you, and @dataclass(frozen=True) makes objects read-only and hashable, which suits test data. Prefer composition (a page has a driver) over inheritance unless "X is a Y" is really true.

How to answer in an interview

Define each pillar in one line and point to your framework: "Pages inherit from BasePage and call super().__init__(driver). Locators are underscore attributes, and tests call methods like login_as(user). BasePage is an ABC with an abstract is_loaded. Any driver from our fixture works, which is polymorphism." (A fixture is a pytest function that prepares things for a test, such as a driver.) If asked about overloading, say clearly that Python does not have it and name the alternatives.

Common mistakes

  • Forgetting super().__init__() in a subclass.
  • Writing self.count += 1 to update a class attribute; it creates an instance attribute instead.
  • Claiming a single underscore makes an attribute private. It is only a convention.
  • Thinking super() always means the direct parent. It follows the MRO.
  • Defining __eq__ and then putting the objects in a set; without __hash__ they are unhashable.

Recap

__init__ sets up an object and self is that object. Encapsulation is by convention, inheritance reuses code, polymorphism comes from overriding and duck typing, and ABCs give abstraction. There is no overloading. Multiple parents are searched in MRO order, and super() moves to the next class in it.

17 questions0/17 solved

StatusQuestionDifficultyAsked in
Not startedOverriding or overloading?EasyAsked in: Accenture, Capgemini, Infosys +2
Not startedThe four OOP pillars in Python with examplesEasy-MediumAsked in: Accenture, Amazon, Capgemini +7
Pro+Overloading versus overriding in PythonEasy-MediumAsked in: Accenture, Capgemini, Cognizant +3
Pro+Abstract classes and interfaces in PythonMediumAsked in: Accenture, Capgemini, Infosys +2
Pro+Abstraction versus encapsulation in PythonMediumAsked in: Capgemini, Coupa, HCLTech
Pro+Multiple inheritance, the diamond problem and the MROMediumAsked in: Accenture, TCS
Pro+super() in a diamondMediumAsked in: Accenture, TCS
Pro+Constructors in Python: __init__, __new__ and super()MediumAsked in: Accenture, Capgemini, Infosys +2
Pro+A child __init__ that skips super()Easy-MediumAsked in: Accenture, Capgemini, Infosys +2
Pro+Count how many objects of a class were createdEasy-Medium
Pro+A subclass that forgets an abstract methodMediumAsked in: Accenture, Capgemini, Infosys +2
Pro+Polymorphism and duck typing in PythonMediumAsked in: Accenture, Capgemini, Cognizant +3
Pro+IS-A versus HAS-A; types of inheritanceEasy-MediumAsked in: TCS, Wipro
Pro+Read-only and immutable classes in PythonMediumAsked in: Accenture, Infosys
Pro+Where have you used OOP in your Python framework?MediumAsked in: Accenture, Capgemini, Salesforce +2
Pro+__str__, __repr__, __eq__ and __hash__Medium
Pro+Can every method be overridden in Python?MediumAsked in: Accenture, Capgemini, Infosys +1
Not startedNot startedIn progressIn progressSolvedSolvedPro+Pro+

The first 2 questions of each topic are free. Pro+ opens every question.

Back to the plan