Level 2 — RecallOOP Fundamentals

OOP Fundamentals

30 minutes40 marksprintable — key stays hidden on paper

Chapter: 2.1 OOP Fundamentals Level: 2 — Recall & Standard Problems Time Limit: 30 minutes Total Marks: 40

Answer all questions. For code questions, write output exactly as Python would print it. Use $...$ conventions only where math is needed.


Q1. Define the difference between a class and an object. Give one real-world analogy. (3 marks)

Q2. What is the purpose of the __init__ method? State one fact about what it can and cannot return. (3 marks)

Q3. Consider the class below. State what A.count and a.count print, and explain the difference between instance and class attributes. (4 marks)

class A:
    count = 0
    def __init__(self):
        A.count += 1
        self.count = 100
 
a = A()
print(A.count)
print(a.count)

Q4. Explain the difference between an instance method, a @classmethod, and a @staticmethod. State what first parameter (if any) each receives. (5 marks)

Q5. What is self in Python? Explain how Python passes it when you call obj.method(). (4 marks)

Q6. Explain name mangling. Given the code below, write the actual attribute name Python creates, and state whether print(obj.__secret) from outside the class works. (4 marks)

class Vault:
    def __init__(self):
        self.__secret = 42

Q7. Rewrite the following so temperature is exposed as a property with a getter and a setter that rejects values below 273-273 (raise ValueError). (5 marks)

class Cell:
    def __init__(self, temp):
        self._temp = temp

Q8. Given the inheritance below, write the MRO of class C (using C.__mro__ names) and state what C().greet() prints. (4 marks)

class A:
    def greet(self): return "A"
class B(A):
    def greet(self): return "B"
class C(B):
    pass

Q9. What does super() do? In the snippet below, what does Dog("Rex").describe() return? (4 marks)

class Animal:
    def __init__(self, name):
        self.name = name
    def describe(self):
        return f"Animal:{self.name}"
class Dog(Animal):
    def describe(self):
        return "Dog-" + super().describe()

Q10. Define duck typing in one sentence, and complete the __add__ and __eq__ dunder methods for the Vec class so that Vec(1,2) + Vec(3,4) == Vec(4,6) evaluates to True. (4 marks)

class Vec:
    def __init__(self, x, y):
        self.x, self.y = x, y
    # __add__ here
    # __eq__ here
Answer keyMark scheme & solutions

Q1. (3 marks)

  • A class is a blueprint/template that defines attributes and behaviour (1)
  • An object is a concrete instance of a class created in memory with its own state (1)
  • Analogy: class = architectural blueprint, object = actual house built from it (also acceptable: cookie-cutter vs cookie) (1)

Q2. (3 marks)

  • __init__ is the constructor/initializer; it runs automatically after object creation to set up initial instance attributes (2)
  • It must return None — it cannot return a value other than None (returning anything else raises TypeError) (1)

Q3. (4 marks)

  • A.count prints 1 (1) — the class attribute was incremented once in __init__
  • a.count prints 100 (1) — an instance attribute count shadows the class attribute
  • Explanation: class attributes are shared across all instances; instance attributes belong to one object. self.count = 100 creates a new instance attribute that hides the class one for lookups on a (2)

Q4. (5 marks)

  • Instance method: receives self (the instance) as first parameter; operates on instance data (1.5)
  • @classmethod: receives cls (the class) as first parameter; can access/modify class state; often used as alternative constructors (1.5)
  • @staticmethod: receives no implicit first parameter; a plain function namespaced inside the class (1.5)
  • Correctly naming first params self / cls / none (0.5)

Q5. (4 marks)

  • self is a reference to the current instance on which a method is called (2)
  • When you call obj.method(), Python translates it to Class.method(obj) — the instance is passed automatically/implicitly as the first argument, bound to self (2)

Q6. (4 marks)

  • Name mangling: any identifier of form __name (two leading underscores, at most one trailing) inside a class is rewritten to _ClassName__name (2)
  • Actual attribute name: _Vault__secret (1)
  • print(obj.__secret) from outside fails with AttributeError, because outside the class __secret is not mangled to the real name (1)

Q7. (5 marks)

class Cell:
    def __init__(self, temp):
        self.temperature = temp      # goes through setter
 
    @property
    def temperature(self):
        return self._temp
 
    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("below absolute zero")
        self._temp = value
  • @property getter returning _temp (1.5)
  • @temperature.setter correctly named (1.5)
  • Validation value < -273 raising ValueError (1.5)
  • Storing in backing attribute _temp (0.5)

Q8. (4 marks)

  • MRO: [C, B, A, object] (2)
  • C().greet() prints/returns "B" (1) — Python searches MRO, C has no greet, B does, so B.greet is used (1)

Q9. (4 marks)

  • super() returns a proxy to call parent (next-in-MRO) class methods without hardcoding the parent name (2)
  • Dog("Rex").describe() returns "Dog-Animal:Rex" (2) — Dog.describe prepends "Dog-" then calls Animal.describe via super, giving "Animal:Rex"

Q10. (4 marks)

  • Duck typing: if an object provides the required method/interface, its actual class doesn't matter — "if it walks and quacks like a duck, treat it as a duck" (1)
  • Code (3):
    def __add__(self, other):
        return Vec(self.x + other.x, self.y + other.y)
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
  • __add__ returning new Vec (1.5), __eq__ comparing both components (1.5)
[
  {"claim": "Q3: A.count=1, a.count=100", "code": "class A:\n    count=0\n    def __init__(self):\n        A.count+=1\n        self.count=100\na=A()\nresult = (A.count==1 and a.count==100)"},
  {"claim": "Q6: mangled name is _Vault__secret", "code": "class Vault:\n    def __init__(self):\n        self.__secret=42\nv=Vault()\nresult = hasattr(v,'_Vault__secret') and getattr(v,'_Vault__secret')==42"},
  {"claim": "Q8: MRO of C is [C,B,A,object]", "code": "class A: pass\nclass B(A): pass\nclass C(B): pass\nresult = [k.__name__ for k in C.__mro__]==['C','B','A','object']"},
  {"claim": "Q9: Dog describe returns Dog-Animal:Rex", "code": "class Animal:\n    def __init__(self,name): self.name=name\n    def describe(self): return f'Animal:{self.name}'\nclass Dog(Animal):\n    def describe(self): return 'Dog-'+super().describe()\nresult = Dog('Rex').describe()=='Dog-Animal:Rex'"},
  {"claim": "Q10: Vec add and eq work", "code": "class Vec:\n    def __init__(self,x,y): self.x,self.y=x,y\n    def __add__(self,o): return Vec(self.x+o.x,self.y+o.y)\n    def __eq__(self,o): return self.x==o.x and self.y==o.y\nresult = (Vec(1,2)+Vec(3,4)==Vec(4,6))"}
]