Magic Methods

Back

Loading concept...

🪄 Python Magic Methods: Teaching Your Objects to Speak, Compare & Calculate

Imagine you have a toy robot. Out of the box, it just sits there. But what if you could teach it to introduce itself, compare itself to other robots, and even add up with another robot to make a super-robot? That’s exactly what Magic Methods do for Python objects!


🎭 The Analogy: Objects as Actors

Think of every Python object as an actor on a stage. Magic methods are the scripts that tell the actor how to:

  • Introduce themselves (__str__, __repr__)
  • Decide who’s taller, older, or better (__eq__, __lt__, etc.)
  • Do math together (__add__, __sub__, etc.)
  • Act like a box holding things (__len__, __getitem__, etc.)
  • Be called like a function (__call__)
  • Be trustworthy and give yes/no answers (__hash__, __bool__)

📖 Chapter 1: __str__ and __repr__ — The Introduction Cards

What’s the Story?

When you meet someone new, they tell you their name. When you print an object, Python asks: “Hey object, how should I introduce you?”

  • __str__ = The friendly introduction for humans
  • __repr__ = The official introduction for developers

Simple Example

class Pet:
    def __init__(self, name, species):
        self.name = name
        self.species = species

    def __str__(self):
        return f"Hi! I'm {self.name} the {self.species}!"

    def __repr__(self):
        return f"Pet('{self.name}', '{self.species}')"

buddy = Pet("Buddy", "Dog")
print(buddy)        # Hi! I'm Buddy the Dog!
print(repr(buddy))  # Pet('Buddy', 'Dog')

When to Use Which?

Method Purpose Audience
__str__ Pretty, readable End users
__repr__ Precise, recreatable Developers

💡 Pro Tip: If you only define one, define __repr__. Python will use it as a fallback for __str__ too!


⚖️ Chapter 2: Comparison Magic Methods — The Judges

What’s the Story?

Imagine two kids arguing about who has more candies. You need a judge to compare them. Comparison magic methods are those judges!

The Six Comparison Operators

graph TD A["Comparison Methods"] --> B["__eq__ =="] A --> C["__ne__ !="] A --> D["__lt__ <"] A --> E["__le__ <="] A --> F["__gt__ >"] A --> G["__ge__ >="]

Simple Example

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __eq__(self, other):
        return self.score == other.score

    def __lt__(self, other):
        return self.score < other.score

    def __le__(self, other):
        return self.score <= other.score

alice = Student("Alice", 90)
bob = Student("Bob", 85)

print(alice == bob)  # False
print(alice > bob)   # True (90 > 85)
print(bob < alice)   # True

Magic Shortcut: @functools.total_ordering

Define just __eq__ and ONE of __lt__, __le__, __gt__, __ge__, and Python fills in the rest!

from functools import total_ordering

@total_ordering
class Student:
    def __init__(self, score):
        self.score = score

    def __eq__(self, other):
        return self.score == other.score

    def __lt__(self, other):
        return self.score < other.score

➕ Chapter 3: Arithmetic Magic Methods — The Calculators

What’s the Story?

What if you could add two piggy banks together to see your total savings? Or multiply a recipe to serve more people? Arithmetic magic methods let objects do math!

The Math Operators

Operator Method Example
+ __add__ a + b
- __sub__ a - b
* __mul__ a * b
/ __truediv__ a / b
// __floordiv__ a // b
% __mod__ a % b
** __pow__ a ** b

Simple Example: A Money Class

class Money:
    def __init__(self, dollars):
        self.dollars = dollars

    def __add__(self, other):
        return Money(self.dollars + other.dollars)

    def __sub__(self, other):
        return Money(self.dollars - other.dollars)

    def __mul__(self, times):
        return Money(self.dollars * times)

    def __str__(self):
        return f"${self.dollars}"

wallet = Money(50)
piggy = Money(30)

print(wallet + piggy)  # $80
print(wallet - piggy)  # $20
print(wallet * 2)      # $100

Reverse Operations (__radd__, __rsub__, etc.)

When Python sees 5 + my_object, it first tries 5.__add__(my_object). If that fails, it tries my_object.__radd__(5).

def __radd__(self, other):
    return Money(self.dollars + other)

# Now this works:
print(10 + wallet)  # $60

📦 Chapter 4: Container Magic Methods — The Storage Boxes

What’s the Story?

Lists, dictionaries, and sets are containers. What if YOUR object could act like a container too? Store things, count them, and let people peek inside!

The Container Methods

graph LR A["Container Methods"] --> B["__len__ → len&#35;40;&#35;41;"] A --> C["__getitem__ → obj[key]"] A --> D["__setitem__ → obj[key] = val"] A --> E["__delitem__ → del obj[key]"] A --> F["__contains__ → &&#35;39;x&&#35;39; in obj"] A --> G["__iter__ → for x in obj"]

Simple Example: A Toy Box

class ToyBox:
    def __init__(self):
        self.toys = []

    def __len__(self):
        return len(self.toys)

    def __getitem__(self, index):
        return self.toys[index]

    def __setitem__(self, index, toy):
        self.toys[index] = toy

    def __contains__(self, toy):
        return toy in self.toys

    def __iter__(self):
        return iter(self.toys)

    def add(self, toy):
        self.toys.append(toy)

box = ToyBox()
box.add("Car")
box.add("Doll")
box.add("Ball")

print(len(box))       # 3
print(box[0])         # Car
print("Doll" in box)  # True

for toy in box:
    print(toy)  # Car, Doll, Ball

📞 Chapter 5: __call__ — The Callable Object

What’s the Story?

Usually, you call functions. But what if your object could be called like a function? That’s __call__!

It’s like having a toy that does something special when you press its button (call it).

Simple Example

class Greeter:
    def __init__(self, greeting):
        self.greeting = greeting

    def __call__(self, name):
        return f"{self.greeting}, {name}!"

say_hello = Greeter("Hello")
say_hi = Greeter("Hi there")

print(say_hello("Alice"))  # Hello, Alice!
print(say_hi("Bob"))       # Hi there, Bob!

Real-World Use: Counters

class Counter:
    def __init__(self):
        self.count = 0

    def __call__(self):
        self.count += 1
        return self.count

click = Counter()
print(click())  # 1
print(click())  # 2
print(click())  # 3

💡 This is how decorators with state often work!


#️⃣ Chapter 6: __hash__ and __bool__ — Trust & Truth

__hash__ — The ID Card

When you put something in a set or use it as a dictionary key, Python needs a unique ID number (hash) for fast lookups.

graph TD A["Object"] --> B["__hash__&#35;40;&#35;41; → integer"] B --> C["Can be in sets"] B --> D["Can be dict key"]

Rules for __hash__:

  1. If two objects are equal (__eq__), they must have the same hash
  2. Hash should never change during the object’s life
  3. If you define __eq__, you should also define __hash__
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __hash__(self):
        return hash((self.x, self.y))

p1 = Point(1, 2)
p2 = Point(1, 2)

points = {p1, p2}
print(len(points))  # 1 (they're equal!)

__bool__ — The Truth Teller

When Python asks “Is this object truthy or falsy?”, it calls __bool__.

class ShoppingCart:
    def __init__(self):
        self.items = []

    def __bool__(self):
        return len(self.items) > 0

    def add(self, item):
        self.items.append(item)

cart = ShoppingCart()

if cart:
    print("Cart has items!")
else:
    print("Cart is empty!")  # This prints

cart.add("Apple")

if cart:
    print("Cart has items!")  # Now this prints

Default Behavior

Has __bool__? Has __len__? Result
Yes - Uses __bool__
No Yes True if len() > 0
No No Always True

🎯 Summary: Your Magic Method Toolkit

Category Methods Purpose
Display __str__, __repr__ How object looks when printed
Compare __eq__, __lt__, __gt__, etc. Compare two objects
Math __add__, __sub__, __mul__, etc. Do arithmetic
Container __len__, __getitem__, __iter__ Act like a list/dict
Callable __call__ Use object like a function
Hash/Bool __hash__, __bool__ Sets, dicts, and if checks

🚀 You’re Now a Magic Method Wizard!

You’ve learned how to give your Python objects superpowers:

  • They can introduce themselves nicely
  • They can compare and compete
  • They can do math together
  • They can hold and organize data
  • They can be called like functions
  • They can be trusted in sets and answer yes/no questions

Go forth and create magical objects! 🪄✨

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.