OOP & Data Classes
Classes, inheritance, and the @dataclass shortcut for the common case of a class that's mostly just data.
Try answering in your head first, then click a question to check the model answer.
Q1.What problem does `@dataclass` solve compared to writing a plain class by hand?(show answer)
It eliminates the repetitive boilerplate of writing __init__ (assigning each parameter to self), __repr__ (a readable string representation), and __eq__ (field-by-field equality) — you just declare the fields with type annotations and the decorator generates all of it.
Q2.Explain Python's Method Resolution Order (MRO) and why it matters for multiple inheritance.(show answer)
MRO is the linearized order Python searches base classes when resolving an attribute or method call, computed via the C3 linearization algorithm. It matters because with multiple inheritance, more than one base class could define the same method — MRO deterministically decides which one wins, and super() follows this same order rather than jumping straight to the immediate parent.
Q3.What's the difference between `@dataclass` and `@dataclass(frozen=True)`?(show answer)
A plain @dataclass generates a mutable class — fields can be reassigned after construction. frozen=True makes instances immutable (assignment after __init__ raises an error) and also makes the class hashable by default (based on its fields), which lets frozen dataclass instances be used as dict keys or set members.
Q4.Why doesn't Python enforce true private attributes the way some other languages do?(show answer)
Python's philosophy is "we're all consenting adults" — access control is a convention, not enforcement. A single underscore (_attr) signals "internal, don't touch" without restricting access; a double underscore (__attr) triggers name mangling mainly to avoid accidental name clashes in subclasses, not to make the attribute truly inaccessible.
