Of course! This is a fundamental concept in Python that often confuses beginners. Let's break down str() and repr() in detail.

The Core Idea: Human vs. Machine
Think of it this way:
str()is for humans. Its goal is to return a readable, user-friendly string representation of an object. It's what you see when youprint()an object.repr()is for developers and the Python interpreter. Its goal is to return an unambiguous, official string representation of an object. Ideally, this string should be a valid Python expression that could recreate the object. It's what you see when you just type the object's name in an interactive shell.
Key Differences at a Glance
| Feature | str() |
repr() |
|---|---|---|
| Primary Goal | Readability for the end-user. | Unambiguity for developers. |
| Audience | General user. | Developer, debugger, interpreter. |
| When Used | print(obj), str(obj), f"..." formatting. |
Interactive shell (>>> obj), repr(obj), debugging. |
| Key Trait | Can be less specific. | Should be as specific as possible, often unique. |
| Fallback | If __str__ is not defined, it calls __repr__. |
If __repr__ is not defined, it uses a default (e.g., <object ... at 0x...>). |
In-Depth Explanation with Examples
Let's explore this with built-in types first, then see how to control it for your own classes.
Built-in Types
Python's built-in types already have both __str__ and __repr__ methods defined.
Example: The String itself

This is a classic example that perfectly illustrates the difference.
my_string = "hello world"
# str() is for readability
str_version = str(my_string)
print(f"str() result: {str_version}")
# Output: str() result: hello world
# repr() is for unambiguous representation
repr_version = repr(my_string)
print(f"repr() result: {repr_version}")
# Output: repr() result: 'hello world' <-- Note the quotes!
str(my_string)gives you the string's content:hello world.repr(my_string)gives you a string that looks like the Python code to create the string:'hello world'. The quotes are crucial because they tell you it's a string literal. If the string contained a quote,repr()would escape it (e.g.,repr('don\'t')would return"'don\\'t'").
Example: Numbers
For numbers, the difference is less dramatic but still exists.
my_num = 42
print(f"str(42): {str(my_num)}")
# Output: str(42): 42
print(f"repr(42): {repr(my_num)}")
# Output: repr(42): 42
In this case, both look the same because 42 is both readable and an unambiguous representation. However, for floats, repr() often shows more precision.

my_float = 3.14159
print(f"str(3.14159): {str(my_float)}")
# Output: str(3.14159): 3.14159
print(f"repr(3.14159): {repr(my_float)}")
# Output: repr(3.14159): 3.14159
Let's see a case where they differ more.
from datetime import datetime
now = datetime.now()
print(f"str(now): {str(now)}")
# Output: str(now): 2025-10-27 10:30:00.123456 (A nice, clean format)
print(f"repr(now): {repr(now)}")
# Output: repr(now): datetime.datetime(2025, 10, 27, 10, 30, 0, 123456)
# (The exact constructor call needed to recreate this object)
Custom Classes: __str__ vs. __repr__
This is where you gain the most control. When you create a class, you can define special methods __str__ and __repr__ to control how your objects are represented as strings.
The "Rule of Thumb" for Implementation:
__repr__(self): Ask yourself, "How could I recreate this object in code?" The return value should ideally be a string that is a valid Python expression.__str__(self): Ask yourself, "What would the user like to see?" The return value should be a clear, concise, and user-friendly description.
Let's create a Person class.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 1. The Developer-Friendly Representation
def __repr__(self):
# The goal: return a string that could recreate the object
return f"Person(name='{self.name}', age={self.age})"
# 2. The User-Friendly Representation
def __str__(self):
# The goal: return a nice, readable string for the user
return f"{self.name} is {self.age} years old."
# --- Let's use it ---
p = Person("Alice", 30)
# When you just type the variable name in a REPL (like a Python shell),
# or use repr(), it calls __repr__
print(repr(p))
# Output: Person(name='Alice', age=30)
# When you print the object or use str(), it calls __str__
print(p)
# Output: Alice is 30 years old.
# What if you forget to define __str__?
class PersonNoStr:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"PersonNoStr(name='{self.name}', age={self.age})"
p_no_str = PersonNoStr("Bob", 25)
print(p_no_str)
# Output: PersonNoStr(name='Bob', age=25)
# Notice! Since __str__ is missing, Python falls back to using __repr__.
# This is a key safety feature.
# What if you define __str__ but not __repr__?
class PersonNoRepr:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} is {self.age} years old."
p_no_repr = PersonNoRepr("Charlie", 40)
print(p_no_repr)
# Output: Charlie is 40 years old.
print(repr(p_no_repr))
# Output: <__main__.PersonNoRepr object at 0x10f8a5d90>
# Since __repr__ is missing, Python uses its default, which is not very helpful.
Summary and Best Practices
-
Always implement
__repr__for your classes. It's incredibly useful for debugging. If your__repr__is good, you often don't even need a__str__. The fallback from__str__to__repr__means your objects will still have a useful representation. -
Implement
__str__when you need a user-friendly display. If your object is meant to be printed to a user (e.g., in a command-line tool or a report), a custom__str__makes the output much cleaner. -
The
__repr__should be unambiguous. If you see two different objects, theirrepr()strings should be different. If you can, make it evaluable (eval(repr(my_object)) == my_objectis a great test, though not always practical). -
The
__str__should be readable. It can omit some technical details that are not relevant to an end-user.
