Senior 5 min · March 06, 2026

Python OOP MRO Failure — Fix super().__init__ in Mixins

AttributeError on a parent attribute? In cooperative inheritance, a missing super().

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Python OOP interviews test if you design software or just write scripts
  • Four pillars: encapsulation, inheritance, polymorphism, abstraction — each solves a concrete problem
  • @classmethod is an alternative constructor; @staticmethod is a namespaced utility
  • super() in multiple inheritance calls the next class in MRO, not necessarily the parent
  • Biggest mistake: forgetting super().__init__() in a diamond chain — breaks initialization silently
Plain-English First

Think of Object-Oriented Programming like building with LEGO. Each LEGO brick type is a 'class' — a blueprint that says what shape the brick is and what it can do. When you actually snap a brick into your model, that's an 'object' — a real thing built from the blueprint. Inheritance is like a special brick that's based on an existing one but has an extra knob. You don't redesign the whole brick; you just extend what's already there.

Python interviews at companies like Google, Stripe, and mid-sized startups almost always include OOP questions — not because the interviewers want you to recite definitions, but because how you talk about OOP reveals whether you actually design software or just write scripts. A candidate who can explain why encapsulation exists (not just what it is) stands out immediately from the crowd who memorised a textbook definition the night before.

OOP solves the problem of code that grows into an unmanageable tangle. Without it, adding a new feature means hunting through hundreds of lines of procedural code, hoping you don't break something else. With OOP, you model your problem as a collection of objects that each own their own data and behaviour — so changes stay contained, reuse becomes natural, and testing gets easier.

By the end of this article you'll be able to explain the four pillars of OOP with real examples, write and debug class hierarchies in Python, spot the classic mistakes candidates make under pressure, and answer the follow-up questions that actually trip people up in interviews.

The Four Pillars — What They Are and Why They Exist

Every OOP interview starts here. Interviewers don't just want the names — they want to see you connect each pillar to a real problem it solves.

Encapsulation bundles data and the methods that act on it into one unit, and hides the messy internals. Think of a TV remote: you press a button, the TV changes channel. You don't need to know about the infrared signal. The remote encapsulates that complexity.

Inheritance lets a new class reuse behaviour from an existing one. Instead of copy-pasting a send_email method into five classes, you put it in a base Notification class and let the others inherit it. Changes in one place propagate everywhere.

Polymorphism means different objects can respond to the same method call in their own way. You call .render() on a Button and on a Chart — Python figures out which version to run. Your calling code doesn't need an if-else chain.

Abstraction hides what the implementation does and exposes only what it accomplishes. An abstract PaymentProcessor class forces every subclass (Stripe, PayPal) to implement .charge(), but callers never care which one they're using.

io/thecodeforge/oop/pillars_demo.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from abc import ABC, abstractmethod

# ABSTRACTION: Defining the interface. 
# We don't care HOW a gateway works, only that it can authorize and capture.
class PaymentGateway(ABC):
    @abstractmethod
    def authorize(self, amount: float) -> bool:
        pass

    @abstractmethod
    def capture(self, transaction_id: str) -> bool:
        pass

# INHERITANCE: Specific implementation of the abstraction.
class StripeGateway(PaymentGateway):
    def __init__(self, api_secret: str):
        # ENCAPSULATION: Internal credential hidden from the public API
        self.__api_secret = api_secret

    def authorize(self, amount: float) -> bool:
        print(f"[Stripe] Authorizing ${amount}...")
        return True

    def capture(self, transaction_id: str) -> bool:
        print(f"[Stripe] Capturing {transaction_id}")
        return True

# POLYMORPHISM: The caller treats all gateways the same.
def process_order(gateway: PaymentGateway, amount: float):
    if gateway.authorize(amount):
        gateway.capture("tx_123")

gateway = StripeGateway(api_secret="sk_test_4eC39HqLyj")
process_order(gateway, 99.99)
Output
[Stripe] Authorizing $99.99...
[Stripe] Capturing tx_123
Interview Gold:
When asked to define a pillar, always follow your definition with 'and that matters because…'. For example: 'Polymorphism means one interface, many implementations — and that matters because it lets you add a new payment provider without touching any of the existing calling code.' That one sentence separates you from every candidate who just recited a definition.
Production Insight
Production codebases that ignore encapsulation leak internal state through getters and setters — refactoring becomes a minefield.
A deep inheritance chain (5+ levels) in a payment system required touching 7 classes to add a simple logging change — composition would have avoided it.
Rule: shallow inheritance, deep composition.
Key Takeaway
Tie each pillar to a concrete failure you've seen or anticipate.
Encapsulation prevents the Stripe secret from leaking into logs.
Polymorphism lets you swap PayPal for Stripe without changing calling code.
And that matters because it reduces blast radius.

Inheritance vs Composition — The Question That Trips Everyone Up

Interviewers love asking 'when would you use inheritance over composition?' because most candidates either go blank or recite 'favour composition over inheritance' without knowing why.

Inheritance models an is-a relationship. A Dog is an Animal. That relationship is rigid — once you inherit from a class, you're locked into its interface and any side-effects of its implementation changes.

Composition models a has-a relationship. A Car has an Engine. You can swap the engine (electric vs petrol) without rewriting the car. This is why composition is usually more flexible.

The practical rule: use inheritance when the child genuinely is a specialised version of the parent and you want to enforce a shared contract. Use composition when you want to combine behaviours, because it keeps each piece small, testable, and swappable.

The classic trap is building deep inheritance chains (six levels deep) only to find that a change in the grandparent breaks three grandchildren in unpredictable ways. Composition avoids that cascade entirely.

io/thecodeforge/oop/composition_vs_inheritance.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import uuid

# COMPOSITION: Behaviours as separate components
class Logger:
    def log(self, message: str):
        print(f"[LOG] {message}")

class Repository:
    def save(self, data: dict):
        print(f"[DB] Saving {data['id']}")

# The Application Service HAS-A logger and HAS-A repository
class UserService:
    def __init__(self, logger: Logger, repo: Repository):
        self.logger = logger
        self.repo = repo

    def create_user(self, username: str):
        user_id = str(uuid.uuid4())
        self.logger.log(f"Creating user {username}")
        self.repo.save({"id": user_id, "username": username})

# Usage
logger = Logger()
repo = Repository()
service = UserService(logger, repo)
service.create_user("dev_forge")
Output
[LOG] Creating user dev_forge
[DB] Saving <uuid>
Watch Out:
Never inherit just to reuse a method. If the child class isn't genuinely a type of the parent, you'll end up with nonsensical relationships like a PDF inheriting from Printer just to get a format() method. That's composition's job — inject a Formatter object instead.
Production Insight
A team inherited from a base 'Entity' class just to get a 'save()' method — then marketing asked for an 'Auditable' feature and the entire hierarchy broke.
The 3-level inheritance chain for 'Notification' caused a 3-day outage when the base class added a new abstract method.
Rule: if you can't state the relationship as 'X is a Y' without hesitation, use composition.
Key Takeaway
Inheritance is for is-a; composition is for has-a.
The moment you need to 'reuse' a method, reach for composition.
Deep inheritance chains ARE the tech debt that silently makes refactoring impossible.
Inheritance vs Composition Decision Tree
IfDoes the new class genuinely 'is a' subtype of the parent (e.g., Cat is an Animal)?
UseUse inheritance. You want to reuse and extend the parent's interface and behaviour.
IfDo you only need to reuse behaviour, not interface (e.g., Car needs Engine's start method)?
UseUse composition. Inject the Engine as a dependency. You can swap implementations later.
IfIs the relationship hierarchical and stable (e.g., Vehicle -> Car -> Sedan)?
UseInheritance works, but keep depth ≤ 3. Beyond that, use composition to avoid fragile base class.
IfAre you combining multiple behaviours (e.g., Logging + Serialization)?
UseComposition with separate Logging and Serialization components, or use mixins (Python) carefully.

Dunder Methods, Properties, and Class vs Static Methods

These three topics show up constantly in Python OOP interviews because they reveal whether you understand Python's OOP model specifically — not just OOP in general.

Dunder (magic) methods let your objects integrate with Python's built-in syntax. Define __str__ so print(my_object) shows something meaningful. Define __eq__ so order_a == order_b compares the right fields. Define __len__ so len(my_cart) works naturally. They're called 'dunder' because they have Double UNDERscores on each side.

Properties (via @property) let you expose a clean attribute interface while hiding validation logic behind it. You read employee.salary like an attribute, but under the hood Python calls a getter method. The caller never sees the implementation change if you add validation later.

Class methods vs static methods trip up almost everyone. A @classmethod receives the class itself as its first argument (cls) — it can create instances, so it's perfect for alternative constructors. A @staticmethod receives nothing special — it's just a utility function that logically belongs inside the class namespace but doesn't need access to the class or instance.

io/thecodeforge/oop/advanced_methods.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class DatabaseConnection:
    _instances = 0

    def __init__(self, connection_string: str):
        self._connection_string = connection_string
        self.__class__._instances += 1

    # Dunder for debugging
    def __repr__(self):
        return f"DatabaseConnection('{self._connection_string}')"

    # Property for controlled access
    @property
    def status(self) -> str:
        return "Connected" if self._connection_string else "Disconnected"

    # Class Method: Alternative Constructor
    @classmethod
    def from_env(cls):
        # In real world: fetch from os.getenv
        return cls("postgres://localhost:5432/forge")

    # Static Method: Pure utility
    @staticmethod
    def is_valid_uri(uri: str) -> bool:
        return uri.startswith("postgres://")

conn = DatabaseConnection.from_env()
print(f"Status: {conn.status}")
print(f"Representation: {repr(conn)}")
print(f"Is Valid? {DatabaseConnection.is_valid_uri('http://bad.uri')}")
Output
Status: Connected
Representation: DatabaseConnection('postgres://localhost:5432/forge')
Is Valid? False
Interview Gold:
When asked about @classmethod vs @staticmethod, say: 'A classmethod is an alternative constructor — it can build and return instances because it has a reference to the class itself via cls. A staticmethod is just a namespaced function — use it when the logic belongs conceptually to the class but doesn't need to touch class or instance state.' Then mention Employee.from_full_name() as your example. Concrete examples win interviews.
Production Insight
A logging system used @staticmethod for formatting, but later needed access to class-configurable log levels — had to refactor to @classmethod, breaking callers.
@Property setters without validation: a production bug where a negative price was stored in the database because the setter didn't validate.
Rule: prefer @classmethod over @staticmethod when the method might ever need class state.
Key Takeaway
Classmethod = alternative constructor (has cls).
Staticmethod = namespaced utility (no cls).
Property = attribute access with hidden computation.
Use @classmethod when you anticipate needing class context later.

MRO, super(), and Multiple Inheritance — Python's Hidden Complexity

Multiple inheritance is rare in practice but almost always shows up in Python OOP interviews because it exposes whether you understand Python's Method Resolution Order (MRO).

When a class inherits from two parents that both define the same method, Python needs a rule for which one wins. That rule is the C3 linearisation algorithm, and the result is visible via ClassName.__mro__. Python reads it left to right — the first class in the MRO that defines the method wins.

super() doesn't just mean 'call the parent class'. In a multiple-inheritance chain, super() calls the next class in the MRO — which might not be the direct parent. This is the cooperative inheritance pattern, and it's why every class in a diamond hierarchy should call super().__init__() if it wants all initialisers to run correctly.

In practice, you'll see multiple inheritance most often with mixins — small classes that add a single, specific behaviour (like LoggingMixin or SerializableMixin) without being a full standalone class. It's composition-flavoured inheritance, and it's elegant when kept small.

io/thecodeforge/oop/mro_super.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base:
    def __init__(self):
        print("Base init")

class MixinA(Base):
    def __init__(self):
        print("MixinA init")
        super().__init__()

class MixinB(Base):
    def __init__(self):
        print("MixinB init")
        super().__init__()

class Diamond(MixinA, MixinB):
    def __init__(self):
        print("Diamond init")
        super().__init__()

# Triggering the diamond
d = Diamond()
print(f"MRO: {[c.__name__ for c in Diamond.mro()]}")
Output
Diamond init
MixinA init
MixinB init
Base init
MRO: ['Diamond', 'MixinA', 'MixinB', 'Base', 'object']
Watch Out:
If any class in a cooperative inheritance chain forgets to call super().__init__(), the MRO chain breaks silently — classes further down the chain never run their initialisers. You'll get AttributeErrors that look completely unrelated to the missing super() call. Always call super().__init__(args, *kwargs) in every class that's meant to participate in multiple inheritance.
Production Insight
A Django model mixin hierarchy of 4 classes had one missing super() — migration scripts failed with no obvious cause.
Flask extension classes use MRO heavily; a custom extension broke when a new version added a dependency mixin.
Rule: in any diamond, every class must participate in cooperative super calls.
Key Takeaway
MRO is C3 linearisation: left-to-right, depth-first, then rightmost.
super() calls the next in MRO, not the literal parent.
Missing super().__init__() anywhere in the chain breaks all downstream initializers.

Abstract Base Classes, Duck Typing, and Protocols

Python's OOP interviews often probe how you handle interfaces without a formal interface keyword. The answer involves Abstract Base Classes (ABCs), duck typing, and the newer Protocol class from typing.

Abstract Base Classes force subclasses to implement certain methods. Use abc.ABC and @abstractmethod to define contracts. If a subclass doesn't implement all abstract methods, it cannot be instantiated — great for enforcing a shared API across a family of classes.

Duck Typing is Python's runtime philosophy: 'If it walks like a duck and quacks like a duck, it's a duck.' No explicit interface needed — just implement the expected methods. This is powerful but fragile: an object lacking a method only fails at runtime when that method is called.

Protocols (from typing.Protocol) are structural subtyping. You define a protocol class with method signatures, and any object that implements those methods satisfies the protocol — at type-check time via mypy or other tools. This is Python's answer to static duck typing.

The interview trick: when asked 'How does Python handle interfaces?' you can say: 'Python uses duck typing at runtime, ABCs for explicit contracts, and Protocols for static structural typing. I pick ABCs when I need runtime enforcement, Protocols when I want static checking without forcing inheritance.'

io/thecodeforge/oop/abc_protocol.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from abc import ABC, abstractmethod
from typing import Protocol

# ABSTRACT BASE CLASS
class Serializable(ABC):
    @abstractmethod
    def to_json(self) -> str:
        pass

    @abstractmethod
    def from_json(self, data: str) -> None:
        pass

class User(Serializable):
    def __init__(self, name: str):
        self.name = name

    def to_json(self) -> str:
        return f'{{"name": "{self.name}"}}'

    def from_json(self, data: str) -> None:
        import json
        self.name = json.loads(data)["name"]

# PROTOCOL for static structural typing
class Drawable(Protocol):
    def draw(self) -> None:
        ...

class Circle:
    def draw(self) -> None:
        print("Draw circle")

class Square:
    def draw(self) -> None:
        print("Draw square")

def render(obj: Drawable) -> None:
    obj.draw()

# Both work without inheriting Drawable
render(Circle())
render(Square())
Output
Draw circle
Draw square
Interview Tip:
When discussing ABCs vs Protocols, say: 'ABCs enforce a contract at runtime — a subclass must implement all abstract methods or it can't be instantiated. Protocols enforce at static analysis time — any object with matching methods satisfies the protocol, no inheritance needed. I choose ABCs when the contract is central to my domain (e.g., all payment gateways must authorize and capture). I choose Protocols when I want to accept any object that behaves a certain way without forcing a class hierarchy (e.g., anything that can draw()).'
Production Insight
A microservice used duck typing for a 'cache' object — changed to RedisCache which lacked a flush_all() method, causing silent data corruption for 2 hours.
Protocols caught the missing method at CI time when a developer refactored the cache interface.
Rule: for any public API, prefer ABCs or Protocols over bare duck typing to catch errors before production.
Key Takeaway
ABCs enforce contracts at runtime.
Protocols enforce at type-check time — no inheritance needed.
Duck typing gives flexibility but can cause runtime surprises.
Choose the one that matches your error tolerance.
ABC vs Protocol vs Duck Typing Decision
IfDo you need runtime enforcement that a subclass implements all required methods (fail-fast on instantiation)?
UseUse Abstract Base Class (ABC). The subclass cannot be created until all abstract methods are defined.
IfDo you want static type checking but allow arbitrary classes that implement the interface without inheritance?
UseUse Protocol. Mypy or pyright will check at analysis time, but no runtime constraint.
IfIs the code written for quick scripts or dynamic environments with minimal type tooling?
UseUse Duck Typing. Just call the methods and handle AttributeError if needed. No formal contract.
IfAre you building an API/library where callers should explicitly derive from your base class?
UseUse ABC. It communicates intent: 'derive from me and implement these methods.'
● Production incidentPOST-MORTEMseverity: high

The Silent super() Break: A MRO Chain Failure

Symptom
AttributeError: 'RefundService' object has no attribute 'db_connection' when calling process_refund(). The attribute was defined in a base class DatabaseConnector, but only failed when RefundService was used through a specific subclass chain involving two mixins.
Assumption
The team assumed that since each mixin called super().__init__() indirectly through their own __init__ chains, the base initializer would always run. But one mixin had a custom __init__ that forgot to call super().__init__(), breaking the MRO chain.
Root cause
In a cooperative inheritance diamond (class RefundService(MixinA, DatabaseConnector)), MixinA's __init__ did not call super().__init__(). The MRO chain stopped at MixinA, so DatabaseConnector.__init__ never executed, leaving self.db_connection unset.
Fix
Add super().__init__(args, *kwargs) as the first line of MixinA.__init__. Also added a regression test that calls super().__init__() in every mixin and verifies that the full MRO chain completes.
Key lesson
  • Every class in a multiple inheritance chain must call super().__init__() unless it's the root (inheriting only from object).
  • Use __mro__ to visualize the chain during debugging — it shows exactly which initializers will run.
  • Never assume that because a parent class exists in the MRO, its __init__ will be called — it only runs if every class in the chain before it properly delegates to super().
Production debug guideSymptom → Action: Common OOP-related runtime failures4 entries
Symptom · 01
AttributeError on an attribute that 'should exist' from parent class
Fix
Check if any class in the inheritance chain overrides __init__ without calling super().__init__(). Print ClassName.__mro__ to see the full chain, then inspect each class's __init__ for missing super() calls.
Symptom · 02
Shared mutable default argument causing data corruption across instances
Fix
Look for def __init__(self, items=[]) in the codebase. Change to items=None and initialize self.items = items if items is not None else [] inside __init__. This is a Python-specific trap that affects all OOP code.
Symptom · 03
Unexpected method resolution — wrong method being called in diamond hierarchy
Fix
Print ClassName.__mro__ and compare with expected order. Ensure all classes in the diamond use cooperative super() calls. If any class breaks the chain, MRO order may produce a different method than expected.
Symptom · 04
repr() output is useless (shows object memory address instead of details)
Fix
Define __repr__ in the class. The rule: __repr__ must be unambiguous and ideally show enough info to recreate the object. Then define __str__ for user-friendly display. Without __repr__, logging and debugging become painful.
★ OOP Debugging Quick ReferenceFast commands and fixes for common Python OOP pitfalls in interviews or production.
Missing attribute from parent after subclass init
Immediate action
Check __init__ in all parent classes for missing super().__init__()
Commands
print(ClassName.__mro__)
python -c "import inspect; inspect.getsource(ClassName.__init__)"
Fix now
Add super().__init__(args, *kwargs) as first line of __init__
Mutable default argument corruption+
Immediate action
Stop using mutable defaults; switch to None sentinel
Commands
grep -rn 'def __init__(.*=\[\|.*={}\|.*=set()' .
sed -i 's/def __init__(self, items=\[\)/def __init__(self, items=None)/g'
Fix now
Assign self.items = items if items is not None else []
repr(obj) shows '<ClassName object at 0x...>'+
Immediate action
Add __repr__ method to return a meaningful string
Commands
class ClassName: def __repr__(self): return f'ClassName(field1={self.field1!r})'
python -c "import logging; logging.warning('repr: %r', obj)"
Fix now
Implement __repr__ now, then __str__ if needed
TypeError: __init__() missing 1 required positional argument+
Immediate action
Check class signature and instantiation call
Commands
import inspect; sig = inspect.signature(ClassName.__init__)
print(f'Expected: {sig.parameters}')
Fix now
Ensure instantiation matches __init__(self, ...) arguments
Method Type Comparison
Feature@classmethod@staticmethodInstance Method
First argumentcls (the class)None (no implicit arg)self (the instance)
Can access instance stateNoNoYes
Can access class stateYesNoYes (via self.__class__)
Can create instancesYes — perfect for thisPossible but awkwardYes
Callable on class directlyYesYesTechnically, but unusual
Primary use caseAlternative constructorsUtility / helper functionsCore object behaviour
Real-world exampleEmployee.from_csv(row)Employee.is_valid_salary(n)employee.get_pay_slip()

Key takeaways

1
The four pillars only matter in interviews when you tie each one to a concrete problem it solves
'encapsulation hides the Stripe API key from the calling code' beats 'encapsulation bundles data and methods' every time.
2
Prefer composition over inheritance when you want to combine behaviours
inject a GPSModule into a SmartCar rather than making SmartCar inherit from GPSModule. Inheritance is for genuine 'is-a' relationships only.
3
@classmethod is Python's pattern for alternative constructors because it receives cls and can therefore build and return an instance
that's the only reason it exists over @staticmethod.
4
In any multiple-inheritance chain, always call super().__init__(args, *kwargs)
forgetting it silently breaks the MRO chain and causes AttributeErrors that look unrelated to the real problem.
5
Choose ABCs for runtime contract enforcement, Protocols for static type checking without inheritance, and duck typing only for quick scripts or when you have thorough testing to catch missing methods early.

Common mistakes to avoid

4 patterns
×

Mutable default argument in __init__

Symptom
All instances of a class share the same mutable default (e.g., list, dict). Adding an item to one instance's attribute magically appears in all other instances, causing data corruption and hard-to-find bugs.
Fix
Use def __init__(self, items=None) then inside the method set self.items = items if items is not None else []. Never use mutable defaults in function signatures.
×

Forgetting to call super().__init__() in a subclass

Symptom
AttributeError on attributes that the parent class was supposed to set. The error only appears in certain code paths when the subclass is instantiated, and it can take hours to trace back to a missing super() call.
Fix
Always include super().__init__(args, *kwargs) as the first line of your subclass __init__, unless you have an explicit reason not to. In a diamond chain, every class must call super().__init__() or the chain breaks.
×

Confusing __str__ and __repr__

Symptom
repr(obj) in logs or REPL shows unhelpful <__main__.Order object at 0x7f3a...> instead of a meaningful representation. Developers waste time inspecting objects manually.
Fix
Always define __repr__ to return an unambiguous string that could recreate the object (e.g., Order(123, 'pending')). Define __str__ for user-friendly display. If only one is defined, Python falls back to __repr__ for __str__, so __repr__ is the priority.
×

Using inheritance just to reuse a method (e.g., class PDF inherits from Printer to use format())

Symptom
Nonsensical class hierarchies that break when the parent class changes. The child inherits interface methods that don't make sense for its domain.
Fix
Use composition instead: inject a Formatter object into the PDF class. You get reusability without the tight coupling and semantic mismatch.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
What is the difference between a classmethod and a staticmethod in Pytho...
Q02SENIOR
Explain Python's Method Resolution Order. If class C inherits from both ...
Q03SENIOR
How does Python enforce encapsulation? What's the difference between a s...
Q04SENIOR
Implement a Singleton pattern in Python using a metaclass or the __new__...
Q05SENIOR
How do Abstract Base Classes (ABCs) differ from Interfaces in Java/C#, a...
Q01 of 05SENIOR

What is the difference between a classmethod and a staticmethod in Python, and can you give a real-world use case where you'd choose each one?

ANSWER
A classmethod takes cls as the first argument, giving it access to class state and the ability to create instances — perfect for alternative constructors like Employee.from_csv(row). A staticmethod takes no implicit first argument — it's just a function namespaced inside the class for logical grouping, e.g., Employee.is_valid_salary(n). Choose classmethod when you need to build instances or access class-level attributes; choose staticmethod for pure utility functions that conceptually belong to the class.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What Python OOP concepts are most commonly tested in interviews?
02
What is the difference between __str__ and __repr__ in Python?
03
How do you implement a private attribute in Python?
04
Is Python truly object-oriented if it doesn't have true private attributes?
05
When should I use an Abstract Base Class (ABC) instead of a regular class?
🔥

That's Python Interview. Mark it forged?

5 min read · try the examples if you haven't

Previous
Top 50 Python Interview Questions
2 / 4 · Python Interview
Next
Python Data Structures Interview Q