Lab 12: OOP — Classes & Objects

🎯 Objective

Design and implement Python classes — encapsulation, constructors, methods, properties, and the special "dunder" methods that power Python's built-in operations.

📚 Background

Object-Oriented Programming (OOP) organizes code around objects — bundles of data (attributes) and behavior (methods). Python is fully object-oriented: even integers and strings are objects. OOP enables encapsulation (hiding implementation details), reuse (class hierarchies), and modelling (representing real-world concepts as code). Python's OOP is more flexible than Java or C++ — you can add attributes dynamically and use duck typing.

⏱️ Estimated Time

40 minutes

📋 Prerequisites

  • Lab 11: List Comprehensions & Generators

🛠️ Tools Used

  • Python 3.12

🔬 Lab Instructions

Step 1: Your First Class

📸 Verified Output:

💡 self is not a keyword — it's just the conventional name for the first parameter that Python passes automatically when calling an instance method. It refers to the object itself.

Step 2: Properties — Controlled Access

📸 Verified Output:

Step 3: Special Methods (Dunder Methods)

📸 Verified Output:

Step 4: Class Methods and Static Methods

📸 Verified Output:

Step 5: Inheritance

📸 Verified Output:

Step 6: Dataclasses (Python 3.7+)

📸 Verified Output:

Step 7: Context Manager Protocol

📸 Verified Output:

Step 8: Putting It All Together

📸 Verified Output:

✅ Verification

Expected output:

🚨 Common Mistakes

  1. Forgetting self: def method(x) instead of def method(self, x) — the first param is always self.

  2. Mutable class variables: class Foo: items = [] — ALL instances share the same list! Use self.items = [] in __init__.

  3. Not calling super().__init__(): In subclasses, always call parent constructor.

  4. Implementing __eq__ without __hash__: If you define __eq__, Python removes __hash__, making objects unhashable in sets/dicts.

  5. __repr__ vs __str__: repr for debugging (machine-readable); str for display (human-readable).

📝 Summary

  • Classes encapsulate data (self.x) and behavior (def method(self))

  • __init__ is the constructor; @property creates computed attributes

  • Dunder methods: __repr__, __str__, __add__, __eq__, __len__ etc.

  • @classmethod receives the class; @staticmethod receives nothing

  • Inheritance: class Child(Parent): super().__init__(...)

  • @dataclass auto-generates __init__, __repr__, __eq__ — use for simple data containers

  • Context managers: __enter__ + __exit__ enable the with statement

🔗 Further Reading

Last updated