OOP Fundamentals
Chapter: 2.1 OOP Fundamentals Level: 1 — Recognition (MCQ, Matching, True/False with justification) Time Limit: 20 minutes Total Marks: 30
Section A — Multiple Choice (1 mark each)
Choose the single best answer.
Q1. Which statement best describes the relationship between a class and an object?
- A) A class is an instance of an object
- B) A class is a blueprint; an object is an instance created from it
- C) They are identical terms in Python
- D) An object defines the class
Q2. What is the primary purpose of the __init__ method?
- A) To destroy an object
- B) To initialize instance attributes when an object is created
- C) To allocate memory for the class
- D) To define class-level constants
Q3. A decorator that lets a method receive the class itself (conventionally cls) rather than an instance is:
- A)
@staticmethod - B)
@property - C)
@classmethod - D)
@abstractmethod
Q4. What does the name-mangled attribute __balance become inside a class named Account?
- A)
__balanceunchanged - B)
_Account__balance - C)
Account__balance - D)
_balance
Q5. In an instance method definition, self refers to:
- A) The class object
- B) The specific instance the method is called on
- C) A reserved keyword forbidden to rename
- D) The parent class
Q6. Which built-in function returns the method resolution order of a class?
- A)
Class.order() - B)
Class.__mro__orClass.mro() - C)
mro(Class.self) - D)
resolve(Class)
Q7. super() is used primarily to:
- A) Create a new subclass at runtime
- B) Call a method of a parent (super)class from a subclass
- C) Convert an instance to a class
- D) Force multiple inheritance
Q8. Which dunder method is invoked by the == operator?
- A)
__equals__ - B)
__cmp__ - C)
__eq__ - D)
__is__
Q9. "Duck typing" means:
- A) Objects must inherit from a common base class to be used interchangeably
- B) An object's suitability is determined by presence of methods/attributes, not its type
- C) All ducks are objects
- D) Type checking is enforced at compile time
Q10. To declare an abstract method you use, from the abc module:
- A)
@abstract - B)
@abstractmethodwith a class inheritingABC - C)
@virtual - D)
@interface
Section B — Matching (1 mark each, 6 marks)
Q11. Match each dunder method (left) to what it customizes (right).
| Dunder | Purpose | |
|---|---|---|
a) __add__ |
1. Length via len() |
|
b) __len__ |
2. Developer-facing unambiguous string | |
c) __str__ |
3. + operator |
|
d) __repr__ |
4. Ordering via < |
|
e) __lt__ |
5. User-friendly string | |
f) __hash__ |
6. Usability in sets/dict keys |
Write your answers as pairs, e.g. a-3.
Section C — True / False WITH Justification (2 marks each: 1 verdict + 1 reason)
Q12. A class attribute is shared across all instances unless an instance shadows it by assignment.
Q13. A @staticmethod receives self as its first argument automatically.
Q14. Using @property allows a method to be accessed like an attribute without parentheses.
Q15. Composition models an "is-a" relationship, while inheritance models a "has-a" relationship.
Q16. In Python's C3 linearization, a class always appears in the MRO before its parents.
Q17. A @dataclass automatically generates an __init__ method based on declared fields.
Q18. Method overriding requires the subclass method to have a different name from the parent method.
(End of paper)
Answer keyMark scheme & solutions
Section A (10 marks)
Q1 — B. A class is a template/blueprint defining structure and behavior; objects are concrete instances built from it. (1)
Q2 — B. __init__ runs right after object creation to set up instance attributes; it is not a constructor of memory (__new__ allocates) but the initializer. (1)
Q3 — C. @classmethod passes the class as cls; static methods get nothing automatic, properties manage attribute access. (1)
Q4 — B. Name mangling transforms __name (double leading underscore, no trailing) to _ClassName__name → _Account__balance. (1)
Q5 — B. self is the instance on which the method was invoked; Python passes it implicitly. It is a convention, not a keyword. (1)
Q6 — B. Class.mro() (a method) or the attribute Class.__mro__ gives the resolution order. (1)
Q7 — B. super() returns a proxy delegating to the next class in the MRO, letting you call parent implementations. (1)
Q8 — C. == dispatches to __eq__. (1)
Q9 — B. Duck typing: "if it walks/quacks like a duck…" — behavior/interface presence matters, not the declared type. (1)
Q10 — B. @abstractmethod on a class deriving from abc.ABC prevents instantiation until the method is overridden. (1)
Section B (6 marks, 1 each)
Q11: a-3, b-1, c-5, d-2, e-4, f-6.
__add__→+,__len__→len(),__str__→ user string,__repr__→ developer string,__lt__→<,__hash__→ set/dict key usability. (1 mark per correct pair)
Section C (14 marks — 1 verdict + 1 justification each)
Q12 — TRUE. Class attributes live on the class; all instances read the same value. Assigning instance.attr = x creates an instance attribute that shadows the class one for that instance only. (1+1)
Q13 — FALSE. A @staticmethod receives no automatic first argument — neither self nor cls. It behaves like a plain function namespaced in the class. (1+1)
Q14 — TRUE. @property turns a method into a managed attribute; accessing it (obj.x) calls the getter automatically without (). (1+1)
Q15 — FALSE. It is reversed: composition = "has-a" (an object holds another as a component), inheritance = "is-a". (1+1)
Q16 — TRUE. C3 linearization preserves local precedence: a class precedes all its ancestors, and parent order is preserved across the hierarchy. (1+1)
Q17 — TRUE. @dataclass auto-generates __init__ (and __repr__, __eq__ by default) from the class's annotated fields. (1+1)
Q18 — FALSE. Overriding requires the same name and compatible signature so the subclass version replaces the parent's in the MRO lookup. (1+1)
[
{"claim":"Name mangling: __balance in class Account becomes _Account__balance",
"code":"class Account:\n def __init__(self):\n self.__balance = 5\na = Account()\nresult = ('_Account__balance' in a.__dict__) and (a._Account__balance == 5)"},
{"claim":"staticmethod receives no implicit first arg; classmethod receives cls",
"code":"class C:\n @staticmethod\n def s(x):\n return x\n @classmethod\n def c(cls):\n return cls.__name__\nresult = (C.s(9) == 9) and (C.c() == 'C')"},
{"claim":"MRO puts subclass before parents in C3 linearization",
"code":"class A: pass\nclass B(A): pass\nmro=[k.__name__ for k in B.mro()]\nresult = (mro.index('B') < mro.index('A')) and (mro[-1]=='object')"},
{"claim":"__eq__ is invoked by == operator",
"code":"class P:\n def __init__(self,v):\n self.v=v\n def __eq__(self,o):\n return self.v==o.v\nresult = (P(3)==P(3)) and not (P(3)==P(4))"},
{"claim":"dataclass auto-generates __init__ from fields",
"code":"from dataclasses import dataclass\n@dataclass\nclass Pt:\n x:int\n y:int\np=Pt(1,2)\nresult = (p.x==1) and (p.y==2)"}
]