Skip to content
CodeKruPro

Predict the output · Python

Classes, static members and inheritance

Overriding, hiding, constructors order and shared state.

Learn: Classes, attributes and inheritance in Python7 minute read

Python classes work differently from Java classes in a few important ways, and interviewers often ask about those differences. Test frameworks are full of classes: a base test class with setup code, page objects that inherit from a base page, small data classes for test data. Output questions usually test attribute lookup, shared class attributes or super(). These rules explain each case.

Two words first

An attribute is a name stored on an object or a class, reached with a dot, as in obj.name. A method is a function defined in a class. Its first parameter, self, is the object the method was called on. A class attribute is defined in the class body and belongs to the class. An instance attribute is set on one object, usually in __init__ (the method that runs when an object is created) with self.name = ....

Rule 1: attribute lookup goes instance, class, parents

Reading obj.name first looks in the object's own attributes, then in its class, then in the parent classes in method resolution order (MRO), which is the order in which Python searches the classes. Methods are found the same way, at the moment of the call. So a method written in a parent class that calls self.speak() runs the child's speak when self is a child object.

Rule 2: class attributes are shared, until an instance hides them

A class attribute is shared by every instance. Assigning through an instance, obj.level = 5, does not change the class attribute; it creates an instance attribute that hides it for that object only. Mutating a shared object is different: self.tags.append(x) on a list defined in the class body changes the one list that every instance sees. Create per-object lists in __init__.

class Base:
    tags = []
    level = 1

    def name(self):
        return "base"

    def show(self):
        return self.name() + str(self.level)

class Child(Base):
    def name(self):
        return "child"

a, b = Child(), Child()
a.level = 5
a.tags.append("x")
print(a.show(), b.show())
print(b.tags)
child5 child1
['x']

Rule 3: a parent's __init__ runs only if you call it

If a child defines __init__, Python runs only that method. The parent's __init__ runs only when the child calls super().__init__(). Forget it, and attributes the parent would set do not exist, so reading them raises AttributeError. If the child defines no __init__, the parent's is inherited and runs. In Java the parent constructor always runs, so this is a common question for people who know both languages.

Rule 4: super() follows the MRO

With multiple inheritance, Python puts all classes in one line, the MRO, which you can see in Class.__mro__. super() means "the next class after this one in the MRO of the object", not simply "my parent". In a diamond, where two parents share a grandparent (class D has parents B and C, and both build on A), that makes every class run once.

class A:
    def __init__(self):
        print("A init")

class B(A):
    def __init__(self):
        print("B init")
        super().__init__()

class C(A):
    pass

B()
C()
print([k.__name__ for k in B.__mro__])
B init
A init
A init
['B', 'A', 'object']

Rule 5: there is no overloading

Defining a method twice in a class keeps only the last definition. Calling it with the argument count of the first version raises TypeError. Python uses default values and *args (a way to accept any number of values) instead.

Rule 6: printing and comparing objects

print(obj) and f-strings use __str__; containers such as lists show their items with __repr__. Without __eq__, == compares identity, so two equal-looking objects are not equal. @dataclass (a decorator, a line above a class that adds features to it) generates __init__, __repr__ and a field-by-field __eq__. __str__, __repr__ and __eq__ are special methods with double underscores that Python calls for you.

Rule 7: classmethod and staticmethod

A classmethod receives the class it was called on, so through a subclass it gets the subclass. A staticmethod receives nothing extra and behaves like a plain function stored on the class.

How to answer in an interview

Say where Python finds each attribute: "a has its own level, 5; b has none, so it finds the class value, 1." For super() questions, write the MRO first, then follow it. If asked about overloading, answer directly that Python has none and show the default-argument alternative.

Common mistakes

  • Expecting obj.attr = x to change the class attribute.
  • Defining a mutable list in the class body for per-object data.
  • Forgetting super().__init__() in a child class.
  • Reading super() as "my parent" in a diamond.
  • Defining a method twice and expecting both versions to exist.

Recap

Lookup goes instance, class, then parents in MRO order. Class attributes are shared, and assignment through an instance only hides them. Parent __init__ runs only when called, and super() follows the MRO. There is no overloading. __str__, __repr__ and __eq__ decide how objects print and compare.

12 questions0/12 solved

Not startedNot startedIn progressIn progressSolvedSolvedPro+Pro+

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

Back to the plan