Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
124: Magic Methods for Arithmetic Operators
I was working on a small financial tool the other day, and I ran into a problem that I think is a perfect way to introduce how Python handles math with custom objects. I had a Money class to track balances, because using raw floats for currency is a recipe for rounding disasters.
The "TypeError" Wall
I started with a simple class. I just wanted to be able to add two money objects together to get a total. Here is what my first attempt looked like:
class Money:
def __init__(self, amount, currency="USD"):
self.amount = amount
self.currency = currency
def __repr__(self):
return f"{self.amount} {self.currency}"
wallet_a = Money(50)
wallet_b = Money(20)
print(wallet_a + wallet_b)
I hit run, and predictably, Python screamed at me: TypeError: unsupported operand type(s) for +: 'Money' and 'Money'. This makes sense. Python knows how to add two integers or two strings, but it has no idea what "adding" two Money objects actually means. Should it add the amounts? Should it concatenate the currency strings? It doesn't want to guess, so it just crashes.
Teaching Python how to add
To fix this, I need to tell Python exactly what to do when it sees the + symbol between two Money instances. This is where "magic methods" (or dunder methods, because of the double underscores) come in. For addition, the method is __add__.
Let's try implementing it. I'll add the logic to the class, but I'll also add a quick check to make sure we aren't trying to add USD to EUR, because that would be a financial nightmare.
class Money:
def __init__(self, amount, currency="USD"):
self.amount = amount
self.currency = currency
def __repr__(self):
return f"{self.amount} {self.currency}"
def __add__(self, other):
if self.currency != other.currency:
raise ValueError("Cannot add different currencies!")
return Money(self.amount + other.amount, self.currency)
wallet_a = Money(50)
wallet_b = Money(20)
print(wallet_a + wallet_b) # Now it prints: 70 USD
That worked. The magic is that when I write wallet_a + wallet_b, Python is actually calling wallet_a.__add__(wallet_b) behind the scenes. I'm effectively "overloading" the operator.
Expanding the toolkit
Of course, once addition works, I realized I also need to be able to subtract and multiply. I could write a method called subtract(), but then I'd have to write wallet_a.subtract(wallet_b), which feels clunky. I want to use - and *.
Following the same pattern as __add__, I can use __sub__ for subtraction and __mul__ for multiplication. Watch how this evolves:
class Money:
def __init__(self, amount, currency="USD"):
self.amount = amount
self.currency = currency
def __repr__(self):
return f"{self.amount} {self.currency}"
def __add__(self, other):
if self.currency != other.currency:
raise ValueError("Currency mismatch")
return Money(self.amount + other.amount, self.currency)
def __sub__(self, other):
if self.currency != other.currency:
raise ValueError("Currency mismatch")
return Money(self.amount - other.amount, self.currency)
def __mul__(self, multiplier):
# Multiplying money by money is weird,
# but multiplying money by a number makes sense (e.g., 3x the cost)
return Money(self.amount * multiplier, self.currency)
price = Money(10)
tax = Money(2)
total = (price + tax) * 3
print(total) # 36 USD
Dealing with the "Wrong" side
Here is where I hit a snag. I tried to do this: print(10 * price). It crashed. Why? Because 10 is an integer, and the integer class doesn't know how to multiply itself by a Money object. Python looks at the left-hand side first, fails, and then looks for a "reflected" version of the method on the right-hand side.
If __mul__ handles Money * something, then __rmul__ (Right-multiply) handles something * Money. I just need to point it back to the original logic.
def __rmul__(self, multiplier):
return self.__mul__(multiplier)
Now 10 * price works exactly like price * 10. It's a small addition, but it makes the class feel like a first-class citizen in the Python language.
📋 Practical Task
Implementing a 2D Vector Math Class
In game development or physics simulations, we often use 2D Vectors to represent positions or velocities. Your task is to build a Vector2D class that allows for intuitive mathematical operations.
Requirements:
- The class should be initialized with
xandycoordinates. - Implement
__add__so that adding two vectors adds their corresponding x and y components (e.g.,Vector2D(1, 2) + Vector2D(3, 4)results in aVector2D(4, 6)). - Implement
__sub__to handle vector subtraction. - Implement
__mul__to allow a vector to be scaled by a number (e.g.,Vector2D(2, 3) * 3results inVector2D(6, 9)). - Implement
__rmul__so that scaling works regardless of whether the number is on the left or the right. - Implement
__repr__so that the vector prints clearly, such asVector(x, y).
There are no comments for now.