Python
OOP
classes
printing
__str__

How to print instances of a class using print?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you call print(obj) on a Python instance and see something like a memory address, Python is falling back to its default object representation. To make instances print cleanly, define __str__ for a readable display and usually __repr__ for a more precise developer-facing representation.

What print() Actually Uses

print(obj) calls str(obj), and str(obj) looks for the object's __str__ method. If __str__ is not defined, Python falls back to __repr__. If neither method is customized, you get the default inherited representation.

That means there are two useful hooks:

  • '__str__ for human-friendly output'
  • '__repr__ for debugging and interactive inspection'

They often return related strings, but they serve different audiences.

Defining __str__

Here is the simplest useful example:

python
1class User:
2    def __init__(self, username, active):
3        self.username = username
4        self.active = active
5
6    def __str__(self):
7        status = "active" if self.active else "inactive"
8        return f"User {self.username} is {status}"
9
10user = User("mark", True)
11print(user)

Output:

text
User mark is active

This is the method to write when you care about readable logs, command-line output, or UI-adjacent text.

Defining __repr__

__repr__ is meant for developers. A good __repr__ makes the object's state easy to inspect.

python
1class User:
2    def __init__(self, username, active):
3        self.username = username
4        self.active = active
5
6    def __repr__(self):
7        return f"User(username={self.username!r}, active={self.active!r})"
8
9user = User("mark", True)
10print(repr(user))

Output:

text
User(username='mark', active=True)

The !r conversion tells the f-string to use repr for each field, which is useful when strings, quotes, or escape sequences need to be unambiguous.

Using Both Together

In real classes, it is common to define both methods:

python
1class Order:
2    def __init__(self, order_id, total):
3        self.order_id = order_id
4        self.total = total
5
6    def __repr__(self):
7        return f"Order(order_id={self.order_id!r}, total={self.total!r})"
8
9    def __str__(self):
10        return f"Order #{self.order_id}: ${self.total:.2f}"
11
12order = Order("A102", 49.5)
13print(order)
14print([order])

Typical output:

text
Order #A102: $49.50
[Order(order_id='A102', total=49.5)]

Notice the difference:

  • 'print(order) uses __str__'
  • printing a container such as a list uses __repr__ for its elements

That is one reason __repr__ remains useful even if __str__ already looks good.

When __repr__ Alone Is Enough

For internal tools or small scripts, you may only define __repr__. Because print() falls back to __repr__, the object still prints sensibly.

python
1class Point:
2    def __init__(self, x, y):
3        self.x = x
4        self.y = y
5
6    def __repr__(self):
7        return f"Point(x={self.x}, y={self.y})"
8
9point = Point(3, 7)
10print(point)

This is often enough when the debugging representation is already readable.

Dataclasses Make This Easier

If your class is mainly data, dataclasses can generate a helpful __repr__ automatically.

python
1from dataclasses import dataclass
2
3@dataclass
4class Product:
5    sku: str
6    price: float
7
8product = Product("BK-100", 19.99)
9print(product)

Output:

text
Product(sku='BK-100', price=19.99)

You can still add a custom __str__ if you want a friendlier format:

python
1from dataclasses import dataclass
2
3@dataclass
4class Product:
5    sku: str
6    price: float
7
8    def __str__(self):
9        return f"{self.sku} costs ${self.price:.2f}"

What Makes a Good Representation

A strong __str__ is brief and readable. A strong __repr__ is precise enough to help debugging. If the object contains sensitive data, be selective about what you include. Printing passwords, tokens, or personal data is an avoidable mistake.

Also avoid doing expensive work in these methods. They may be called from logs, exceptions, debuggers, and interactive inspection more often than you expect.

Common Pitfalls

  • Defining __str__ but forgetting __repr__, then wondering why lists or debuggers still show an unhelpful default format.
  • Returning something other than a string from __str__ or __repr__. Python requires both methods to return str.
  • Building representations that expose secrets such as access tokens or raw personal data. Treat object printing as part of your logging surface.
  • Making __str__ do expensive database queries or other heavy work. String conversion should stay cheap and predictable.
  • Assuming print(obj) uses __repr__ first. It uses __str__ if available and only falls back to __repr__ otherwise.

Summary

  • 'print(obj) uses __str__, and falls back to __repr__ if needed.'
  • Define __str__ for human-friendly output and __repr__ for debugging-oriented output.
  • Containers and debuggers often rely on __repr__, so it is worth implementing well.
  • 'dataclass gives you a useful default __repr__ with very little code.'
  • Keep printed representations safe, concise, and cheap to compute.

Course illustration
Course illustration

All Rights Reserved.