📝 Python

The `__str__` Method: Beautiful Object Output 🎨

0
Author
04e5cc8b-58ac-4bdc-bdee-661bbb
📅
Published
03.04.2026
⏱️
Reading time
5 min
👁️
Views
96
🌱
Level
Beginner

Imagine: you print a zombie to the console and see: <__main__.Zombie object at 0x10e8c4d90> 😱

Useless! You want to see the name, health, status. That’s exactly what the magic method __str__ is for ✨

🤔 The problem: ugly output

class Zombie:
    def __init__(self, name, health):
        self.name = name
        self.health = health

walker = Zombie("Walker", 50)
print(walker)
# <__main__.Zombie object at 0x10e8c4d90>  ← Useless!

What’s wrong?
Python doesn’t know how to display your class. It falls back to showing the memory address.

✨ The fix: __str__

__str__ is a magic method called by print(object) or str(object).

class Zombie:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def __str__(self):  # ← Magic method!
        return f"Zombie '{self.name}' (HP: {self.health})"

walker = Zombie("Walker", 50)
print(walker)
# Zombie 'Walker' (HP: 50)  ← Clean! ✨

What changed?
- Added the __str__ method
- It returns a string (return is required)
- Python automatically calls it when you print() the object

🎨 Examples of nice output

Example 1: Zombie with status indicator

class Zombie:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def __str__(self):
        status = "🟢" if self.health > 30 else "🔴"
        return f"{status} Zombie '{self.name}' | HP: {self.health}"

walker = Zombie("Walker", 50)
runner = Zombie("Runner", 20)

print(walker)  # 🟢 Zombie 'Walker' | HP: 50
print(runner)  # 🔴 Zombie 'Runner' | HP: 20

Example 2: Human with inventory

class Human:
    def __init__(self, name, health):
        self.name = name
        self.health = health
        self.ammo = 12
        self.kills = 0

    def __str__(self):
        return (f"👤 {self.name}\n"
                f"   ❤️  HP: {self.health}/100\n"
                f"   🔫 Ammo: {self.ammo}\n"
                f"   💀 Kills: {self.kills}")

rick = Human("Rick", 85)
rick.kills = 3
print(rick)
# 👤 Rick
#    ❤️  HP: 85/100
#    🔫 Ammo: 12
#    💀 Kills: 3

Example 3: Weapon

class Weapon:
    def __init__(self, name, damage, ammo):
        self.name = name
        self.damage = damage
        self.ammo = ammo

    def __str__(self):
        return f"🔫 {self.name} (Damage: {self.damage}, Ammo: {self.ammo})"

pistol = Weapon("Pistol", 20, 12)
rifle = Weapon("Rifle", 50, 30)

print(pistol)  # 🔫 Pistol (Damage: 20, Ammo: 12)
print(rifle)   # 🔫 Rifle (Damage: 50, Ammo: 30)

🔄 Ways to use __str__

1. With print()

walker = Zombie("Walker", 50)
print(walker)  # Automatically calls walker.__str__()

2. With str()

walker = Zombie("Walker", 50)
text = str(walker)  # Calls walker.__str__()
print(text)  # Zombie 'Walker' (HP: 50)

3. Inside f-strings

walker = Zombie("Walker", 50)
message = f"A {walker} appeared!"
print(message)
# A Zombie 'Walker' (HP: 50) appeared!

4. In lists

zombies = [
    Zombie("Walker", 50),
    Zombie("Runner", 30),
    Zombie("Tank", 100)
]

for zombie in zombies:
    print(zombie)
# Zombie 'Walker' (HP: 50)
# Zombie 'Runner' (HP: 30)
# Zombie 'Tank' (HP: 100)

📋 Rules for __str__

✅ Do this:

1. Always return a string

def __str__(self):
    return f"Zombie '{self.name}'"  # ✅ A string

2. Keep it short and informative

def __str__(self):
    return f"{self.name} (HP: {self.health})"  # ✅ Clear

3. Make it human-readable

def __str__(self):
    return f"👤 {self.name} | ❤️ {self.health}"  # ✅ Friendly

❌ Avoid this:

1. Using print instead of return

def __str__(self):
    print(f"Zombie '{self.name}'")  # ❌ print instead of return!
    # Returns None!

2. Returning a non-string

def __str__(self):
    return self.health  # ❌ An integer, not a string!

3. Dumping too much data

def __str__(self):
    return f"""
    Name: {self.name}
    Health: {self.health}
    Speed: {self.speed}
    Created: {self.created_at}
    Last updated: {self.updated_at}
    ...50 more lines...
    """  # ❌ Way too much!

🪄 Other magic methods

__str__ is not the only magic method — there are many:

__repr__ — for debugging

class Zombie:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def __str__(self):
        return f"Zombie '{self.name}'"  # For the end user

    def __repr__(self):
        return f"Zombie(name='{self.name}', health={self.health})"  # For the developer

walker = Zombie("Walker", 50)
print(walker)        # Zombie 'Walker'                      ← calls __str__()
print(repr(walker))  # Zombie(name='Walker', health=50)     ← calls __repr__()

The difference:
- __str__ — pretty output for the user
- __repr__ — precise representation for the developer (should be unambiguous)

__len__ — for len()

class Horde:
    def __init__(self):
        self.zombies = []

    def add(self, zombie):
        self.zombies.append(zombie)

    def __len__(self):  # ← For len()
        return len(self.zombies)

horde = Horde()
horde.add(Zombie("Walker", 50))
horde.add(Zombie("Runner", 30))

print(len(horde))  # 2  ← calls horde.__len__()

__eq__ — for == comparison

class Zombie:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def __eq__(self, other):  # ← For ==
        return self.name == other.name and self.health == other.health

z1 = Zombie("Walker", 50)
z2 = Zombie("Walker", 50)
z3 = Zombie("Runner", 30)

print(z1 == z2)  # True  ← calls z1.__eq__(z2)
print(z1 == z3)  # False

Common magic methods at a glance:

Method Triggered by Purpose
__init__ Zombie() Object creation
__str__ print(obj), str(obj) User-friendly string
__repr__ repr(obj) Debug-friendly string
__len__ len(obj) Object length
__eq__ obj1 == obj2 Equality check
__lt__ obj1 < obj2 Less-than comparison
__add__ obj1 + obj2 Addition
__getitem__ obj[index] Index access

(More to come in later lessons)

💡 When to use __str__?

✅ Use it when:

  • You need to print the object to the console
  • You want to debug your code (inspect object state)
  • You’re building a game (displaying characters, items)
  • You’re writing logs (recording object state)

Example: debugging a battle

class Zombie:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def __str__(self):
        return f"Zombie '{self.name}' (HP: {self.health})"

    def take_damage(self, damage):
        self.health -= damage
        print(f"{self} took {damage} damage")
        #    ↑ Uses __str__!

walker = Zombie("Walker", 50)
walker.take_damage(20)
# Zombie 'Walker' (HP: 30) took 20 damage  ← Clean output!

⚠️ Common mistakes

Mistake 1: print instead of return

class Zombie:
    def __str__(self):
        print(f"Zombie '{self.name}'")  # ❌ print!

walker = Zombie("Walker", 50)
print(walker)
# Zombie 'Walker'
# None  ← returned None!

Fix:

def __str__(self):
    return f"Zombie '{self.name}'"  # ✅ return

Mistake 2: Returning a non-string

class Zombie:
    def __str__(self):
        return self.health  # ❌ Integer!

walker = Zombie("Walker", 50)
print(walker)  # TypeError: __str__ returned non-string

Fix:

def __str__(self):
    return str(self.health)   # ✅ Convert to string
    # Or
    return f"HP: {self.health}"  # ✅ f-string

Mistake 3: Forgot self

class Zombie:
    def __str__():  # ❌ No self!
        return "Zombie"

walker = Zombie("Walker", 50)
print(walker)  # TypeError

Fix:

def __str__(self):  # ✅ Added self

📝 Checklist

  • [ ] I know why __str__ is needed
  • [ ] I understand that __str__ must return a string
  • [ ] I can define __str__ in a class
  • [ ] I know that __str__ is called by print()
  • [ ] I understand the difference between __str__ and __repr__
  • [ ] I can create nice output with emoji and formatting

🚀 Summary

__str__ is a magic method for pretty-printing objects.

Syntax:

class ClassName:
    def __str__(self):
        return "string describing the object"

Usage:

print(object)   # Calls __str__()
str(object)     # Calls __str__()
f"{object}"     # Calls __str__()

Rules:
- ✅ Always returns a string (return, not print)
- ✅ Short and informative
- ✅ Human-readable

Other magic methods:
- __repr__ — for debugging (precise representation)
- __len__ — for len(object)
- __eq__ — for object1 == object2

Why bother?
Pretty output simplifies debugging and makes your code easier to understand! 🎨

Next step: Learn how to persist data to files! 💾

Your reaction to the article

💬 Comments (0)

🔐 Sign in to leave a comment
🚪 Login
💭

No comments yet

Be the first to share your opinion about this article!

🔗 Similar

Similar articles

Continue learning with these materials

📝

Setting Up Your Environment: Python, pip, and VS …

Before writing code locally, you need to set up three tools: Python, pip, and VS...

📅 04.06.2026 👁️ 17
📝

The datetime Module: Working with Dates and Times

datetime is Python's standard module for working with dates and times. It's part of the...

📅 08.05.2026 👁️ 67
📝

.env Files and Environment Variables: Keeping Sec…

Imagine you wrote a program with an API key hardcoded in the source and pushed...

📅 08.05.2026 👁️ 76

Did you like the article?

Subscribe to our updates and receive new articles first. Grow with PyLand!