5. OOP (Object-Oriented Programming)

Model data and behavior with classes; add Pythonic touches via dunder methods, properties, and dataclasses.

Question: What's the difference between a class method, static method, and instance method?

Answer:

  • Instance Method: The most common. Takes self as the first argument to access instance attributes.

  • @classmethod: Takes cls (the class) as the first argument. Can work with class-level data or be used as an alternative constructor.

  • @staticmethod: Does not take self or cls. It is essentially a regular function namespaced within the class.

Question: What are dunder methods? Give an example.

Answer: Dunder (short for "double underscore") methods, like __init__, __str__, and __len__, are special methods that let you customize your objects to work with Python's built-in functions. For example, defining __len__ lets you call len(my_obj).

Question: What's the difference between __repr__ and __str__?

Answer: __repr__ is an unambiguous representation for developers (debugging), while __str__ is a user-friendly display string.

class User:
    def __init__(self, name: str):
        self.name = name
    def __repr__(self) -> str:
        return f"User(name={self.name!r})"
    def __str__(self) -> str:
        return self.name

Question: What is inheritance?

Answer: Inheritance allows a new class (the child class) to inherit attributes and methods from an existing class (the parent class). This promotes code reuse. Use super() to call methods from the parent class.

Question: What are dataclasses and when would you use them?

Answer: dataclasses generate boilerplate (like __init__, __repr__, comparisons) for classes that mainly store data.

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p = Point(1, 2)  # auto __init__ and __repr__

Question: How do you make dataclasses immutable or ordered?

Answer: Pass options like frozen=True (immutable), order=True (comparison methods), slots=True (lower memory).

@dataclass(frozen=True, order=True)
class User:
    name: str
    age: int

Question: What is @property and why use it?

Answer: @property turns a method into an attribute with optional validation via a setter, providing a stable public API while controlling access.

class User:
    def __init__(self, name: str):
        self._name = name
    @property
    def name(self) -> str:
        return self._name
    @name.setter
    def name(self, value: str) -> None:
        if not value:
            raise ValueError("empty name")
        self._name = value

Question: Composition vs inheritance?

Answer: Prefer composition (have objects that reference other objects) to reuse behavior flexibly. Inheritance is for "is-a" relationships; overuse leads to tight coupling.