Special (Magic) Methods: `__str__` and `__repr__`
We've created a Car class that holds data (attributes) and has behaviors (methods). But what happens when we try to print() one of our Car objects directly?
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
my_car = Car("Toyota", "Camry", 2023)
print(my_car)
Output:
<__main__.Car object at 0x10e8b6d90>
This output isn't very useful. It's the default string representation for a custom object, telling us the class name and where it's located in memory. To make this more informative, we need to implement some of Python's special methods, also known as "magic methods" or "dunder methods" (because they have double underscores).
This article focuses on two of the most important special methods: __str__ and __repr__.
📚 Prerequisites
You should be comfortable creating a basic class with an __init__ method and instance attributes.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ What Special Methods Are: Understand that these "dunder" methods let your objects interact with built-in Python operations.
- ✅
__str__(): How to create a user-friendly, readable string representation of your object for end-users. - ✅
__repr__(): How to create an unambiguous, official string representation of your object for developers. - ✅ The Key Difference: When to use one versus the other, and how they work together.
🧠 Section 1: __str__ - For a "Pretty" String
The goal of __str__ is to return a string that is readable and user-friendly. It's intended for the end-user of your program.
The print() function and the str() constructor both look for and use the __str__ method if it exists.
Let's add a __str__ method to our Car class.
# str_example.py
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
# This is the __str__ special method
def __str__(self) -> str:
"""Returns a user-friendly string representation of the Car."""
return f"A {self.year} {self.make} {self.model}"
my_car = Car("Toyota", "Camry", 2023)
# Now, print() will use our custom __str__ method
print(my_car)
# The str() function also uses it
car_description = str(my_car)
print(car_description)
Output:
A 2023 Toyota Camry
A 2023 Toyota Camry
This is much more useful! We now have full control over how our object is displayed to the user.
💻 Section 2: __repr__ - For a Developer-Focused String
The goal of __repr__ (short for "representation") is to return a string that is unambiguous and, ideally, could be used to recreate the object. It's intended for the developer.
The repr() function calls this method. It's also what you see when you inspect an object in an interactive Python shell. The golden rule for __repr__ is: make it look like a valid Python expression that could create the object.
Let's add a __repr__ method to our Car class.
# repr_example.py
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def __str__(self) -> str:
"""Returns a user-friendly string representation."""
return f"A {self.year} {self.make} {self.model}"
# This is the __repr__ special method
def __repr__(self) -> str:
"""Returns an unambiguous string representation of the Car."""
# Note: This looks like the code you'd use to create the object
return f"Car(make='{self.make}', model='{self.model}', year={self.year})"
my_car = Car("Toyota", "Camry", 2023)
# Let's see the output of repr()
developer_view = repr(my_car)
print(developer_view)
Output:
Car(make='Toyota', model='Camry', year=2023)
This output is perfect for debugging. A developer can see exactly what class the object is and what data was used to create it.
🛠️ Section 3: How They Work Together
So, what's the relationship between the two?
- When you
print()an object, Python looks for a__str__method. If it finds one, it uses it. - If Python cannot find a
__str__method, it will fall back and use the__repr__method instead. - When you inspect an object directly in a shell or call
repr(), Python will only look for__repr__.
Because of this fallback behavior, a good rule of thumb is:
Always implement
__repr__. Implement__str__only when you need a "pretty" string representation that is different from the developer representation.
If you only implement one, it should be __repr__, because it will be used for both user-facing and developer-facing contexts.
# repr_fallback_example.py
class Book:
def __init__(self, title):
self.title = title
# This class only has __repr__
def __repr__(self) -> str:
return f"Book(title='{self.title}')"
my_book = Book("Dune")
# Since there is no __str__, print() will use __repr__ as a fallback.
print(my_book)
Output:
Book(title='Dune')
✨ Conclusion & Key Takeaways
Special methods like __str__ and __repr__ allow your custom objects to integrate seamlessly with built-in Python functions and syntax. They give you control over how your objects are represented, which is crucial for creating readable and debuggable code.
Let's summarize the key takeaways:
__str__is for users: It should be readable and provide a nice, informal representation. It's called byprint()andstr().__repr__is for developers: It should be unambiguous and look like a valid Python expression that could recreate the object. It's called byrepr()and used as a fallback for__str__.- Implement
__repr__first: It's the more fundamental of the two and provides a solid default.
Challenge Yourself:
Create a Person class with name and age attributes. Implement both a __str__ and a __repr__ method for it. The __str__ should be something friendly like "Alice is 30 years old.", while the __repr__ should be the developer-friendly Person(name='Alice', age=30).
➡️ Next Steps
We've now covered the absolute basics of creating a class. In the next articles, we'll explore more advanced OOP concepts, starting with "Instance, Class, and Static Methods" to understand the different kinds of behaviors you can attach to your classes.
Happy coding!