Level 1 — RecognitionOOP Fundamentals

OOP Fundamentals

20 minutes30 marksprintable — key stays hidden on paper

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) __balance unchanged
  • 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__ or Class.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) @abstractmethod with a class inheriting ABC
  • 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)"}
]