๐ Day 17 : OOP Advanced
๐ฏ Enterprise Objective
To write truly Pythonic code, your custom classes should behave like built-in types. Today we unlock 'Dunder' magic methods to make our objects printable, measurable, and iterable. We also explore the elegant @property decorator for bulletproof data validation.
๐ Strategic Overview
| # | Topic | Concept |
|---|---|---|
| 1 | Dunders | str, len, eq |
| 2 | Properties | Encapsulation, @property |
| 3 | Class Methods | @classmethod, @staticmethod |
1. Dunder Methods : Magic Integration
Dunder (Double Under) methods are special methods like str or len that allow your objects to integrate seamlessly with Python's built-in functions. They define how an object should behave when printed, added, or measured.
| Method | Triggered By | Purpose |
|---|---|---|
str | print(obj) | User-friendly string representation |
repr | repr(obj) | Developer-friendly representation (for debugging) |
len | len(obj) | Return length or size of object |
eq | obj == other | Custom equality checking |
๐ผ Why Data Analysts Care
โข Beautiful Logging: Implementing str so print(user) shows 'Alice' instead of <main.User object at 0x...>
โข Custom Collections: Implementing len on a custom Dataset class so len(df) works naturally
โ ๏ธ Missing __repr__
Always implement at least repr. If str is missing, Python falls back to repr. If both are missing, you get the ugly memory address string.
๐งช Concept Checks: Dunder Methods
Q1. Create a Point(x,y) class. Try to print() an instance and note the ugly output.
Q2. Add a str method to Point that returns "(x, y)". Print the instance again.
Q3. Add an eq method that returns True if both x and y are the same. Test Point(1,1) == Point(1,1).
Q4. Add a add method that allows you to add two points together: Point(x1+x2, y1+y2). Test p1 + p2.
Q5. Why is repr meant for developers and str meant for users?
2. Properties & Encapsulation : Safe Data Access
Encapsulation is the practice of hiding internal state. In Python, we prefix private variables with an underscore _. To provide controlled access to these variables, we use the @property decorator, which allows methods to be accessed like attributes.
class User:
def __init__(self):
self._score = 0 # Private variable
@property
def score(self):
return self._score # Accessed without ()
๐ผ Why Data Analysts Care
โข Data Validation: Preventing a user's age from being set to a negative number via a property setter
โข Computed Properties: Calculating fullname dynamically from first_name and last_name without storing it
๐ง Pro Tip
Python doesn't have strict 'private' variables like Java. The underscore is a 'gentleman's agreement' among developers: 'This is internal, do not touch it directly.'
๐งช Concept Checks: Properties
Q1. Create a Person class with an init that sets self.first and self.last.
Q2. Add a @property method called fullname that returns first + " " + last.
Q3. Create an instance, change first, and then print fullname without parentheses. See how it updates.
Q4. Add a setter @fullname.setter that takes a full name string, splits it, and updates first and last.
Q5. Why are properties better than Java-style get_name() and set_name() methods?
3. Class & Static Methods : Alternative Constructors
@classmethod takes cls instead of self and modifies class-level state. It's heavily used for alternative constructors. @staticmethod takes neither and is just a regular function that is logically bundled inside the class.
| Decorator | First Arg | Purpose |
|---|---|---|
| (None) | self | Manipulate instance state |
@classmethod | cls | Manipulate class state / Alternative constructors |
@staticmethod | (None) | Utility functions that don't need class/instance state |
๐ผ Why Data Analysts Care
โข API Parsers: User.from_json(json_string) is a classic use of @classmethod to instantiate objects from APIs
โข Utilities: MathUtils.is_prime(n) is a classic @staticmethod
๐ง Pro Tip
If your method doesn't use self anywhere inside it, your IDE will usually suggest converting it to a @staticmethod.
๐งช Concept Checks: Class Methods
Q1. Create a Date class with day, month, year. Add a @classmethod from_string(cls, date_str) that parses "DD-MM-YYYY".
Q2. Test the class method: d = Date.from_string("25-12-2023"). Print its year.
Q3. Add a @staticmethod is_valid_month(m) that checks if 1 <= m <= 12. Test it.
Q4. Explain the difference between cls in a class method and self in an instance method.
Q5. Add a class attribute count = 0. Increment it inside init. Create 3 objects and print Date.count.
๐ ๏ธ Professional Practice Tasks
Theory is useless without muscle memory. Complete these tasks to solidify your understanding.
Task 1 (Vector Math): Create a Vector(x, y) class. Implement add (add two vectors), sub (subtract), and str (print like ). Test them.
Task 2 (Safe Wallet): Create a Wallet class. Add a private _balance starting at 0. Add a @property for balance. Add a @balance.setter that prevents setting the balance to a negative number. Raise ValueError if attempted.
Task 3 (DataFrame Mock): Create a MockDF class initialized with a list of dictionaries. Implement len to return row count. Implement getitem(key) to return a list of values for a specific column key.
Task 4 (User Parser): Create a User(username, email) class. Add a @classmethod from_csv(cls, csv_line) that takes a string 'bob,bob@gmail.com', splits it, and returns a new User instance.
Task 5 (String Utilities): Create a StringUtils class. Add @staticmethods for is_palindrome(s) and count_vowels(s). Call them without instantiating the class.
๐ป Pure Coding Interview Questions
Q1.
Write a Point(x, y) class. Implement str to return '(x, y)' and repr to return 'Point(x, y)'. Print both.
Q2.
Write a Vector(x, y) class that implements add to add two vectors. Test with Vector(1,2) + Vector(3,4) and print.
Q3.
Write a class Config that implements getitem and setitem using an internal dict. Test with cfg['key'] = 'value'.
Q4.
Write a Temperature class with a @property celsius and a computed @property fahrenheit. Demonstrate updating celsius and reading fahrenheit.
Q5.
Write a Date class with @classmethod from_string(cls, s) and @staticmethod is_valid_year(y). Demonstrate both.
Q6.
Write a User class with @classmethod from_json(cls, json_str) that parses a JSON string into a User object. Test it.
Q7.
Write a class with __name (double underscore). Print dir(obj) to find the mangled name _ClassName__name. Access it.
Q8.
Write a Timer context manager class implementing enter and exit. Use it with with Timer(): time.sleep(0.5).
Q9.
Write a simple descriptor class PositiveNumber with get and set that rejects negative values. Use it in a Product class for price.
Q10.
Write a class implementing iter and next to iterate over a range of even numbers. Test with for n in EvenRange(10):.
Q11.
Write a Greeter class with call(self, name) so instances can be called like functions: g = Greeter(); g('Alice').
Q12.
Write a Singleton class using new: override it to return the same instance every time. Create two objects and verify a is b.
Q13.
Write a Singleton class overriding new to always return the same instance. Create a = S(); b = S() and print a is b.
Q14.
Write a class using slots = ['x', 'y']. Try to add a dynamic attribute obj.z = 1 and catch the AttributeError.
Q15.
Write code comparing memory usage of a class with slots vs without using sys.getsizeof(). Print both sizes.
Q16.
Write a class with getattr that returns 'not found' for any missing attribute. Test with obj.nonexistent.
Q17.
Write a class with getattr (missing attrs only) vs getattribute (all attr access). Show how the latter intercepts everything.
Q18.
Write a class with del that prints 'Deleted'. Create an instance, delete it with del obj, and observe the output.
Q19.
Write a Person class with _age. Add a @age.setter that raises TypeError if the value is not an int. Test it.
Q20.
Write two unrelated classes with len and getitem. Pass both to a function that calls len() and [0] on them โ demonstrating duck typing.
Q21.
Write a class HexInt(int) that overrides str to return the hex representation. Test with print(HexInt(255)).
Q22.
Import abc. Write an ABC Shape with @abstractmethod area(). Create Circle(Shape) implementing area(). Show what happens if you don't implement it.
Q23.
Write an ABC DataReader with @abstractmethod read(). Write CSVReader(DataReader) and JSONReader(DataReader) implementing it.
Q24.
Write a class with diamond inheritance (A -> B, A -> C, B,C -> D). Call D.mro() and print the resolution order.
Q25.
Write a class using @functools.cached_property for an expensive computation. Show it only computes once by adding a print statement inside.
๐ Day 17 Executive Summary
| # | Topic | Key Takeaway |
|---|---|---|
| 1 | Dunder | Makes objects act like native Python types |
| 2 | Property | Protects private data while keeping syntax clean |
| 3 | Classmethod | Perfect for alternative constructors (from_json) |
โ Instructor's End-of-Day Checklist
โข [ ] I can implement str and repr.
โข [ ] I can use @property for getter/setter validation.
โข [ ] I understand when to use @classmethod.