Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
123: Magic Methods: __str__ and __repr__
I was working on a small project the other day—a basic library catalog—and I hit a wall that every Python developer hits eventually. It’s not a bug that crashes the program, but a "developer experience" bug. Let me show you what I mean.
The "Ugly Object" Problem
I started by defining a simple Book class. I just wanted to keep track of the title and the author. Here is the bare-bones version I wrote:
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
my_book = Book("The Left Hand of Darkness", "Ursula K. Le Guin")
print(my_book)
When I ran this, I expected to see the title or maybe the author. Instead, Python gave me this:
<__main__.Book object at 0x7fb8c1234560>
Technically, Python is being honest. It's telling me this is a Book object located at a specific memory address. But as a human, this is useless. If I have a list of a hundred books and I'm trying to debug why one is missing, staring at hex codes isn't going to help me.
Making it Human-Readable
I remember that Python has these "magic methods" (or dunder methods) that allow us to hook into built-in behavior. To fix the print() output, I need __str__. This method is specifically designed to return a "pretty" string for the end-user.
Let's try adding it:
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f"'{self.title}' by {self.author}"
my_book = Book("The Left Hand of Darkness", "Ursula K. Le Guin")
print(my_book) # Output: 'The Left Hand of Darkness' by Ursula K. Le Guin
That's much better. Now, when I call print(), Python looks for __str__ and uses that return value. But wait—I noticed something strange. When I looked at the object in the interactive console (the REPL) or put it inside a list, the "ugly" hex code came back.
books = [Book("1984", "George Orwell"), Book("Brave New World", "Aldous Huxley")]
print(books)
# Output: [<__main__.Book object at 0x...>, <__main__.Book object at 0x...>]
Why did __str__ work for a single object but fail inside the list? It's because print(list) doesn't call __str__ on the items inside; it calls __repr__.
The Developer's Perspective
This is where __repr__ comes in. While __str__ is for the user, __repr__ (short for representation) is for the developer. Its goal is to be unambiguous. Ideally, the string returned by __repr__ should look like the Python code used to create the object.
Let's refine the class again:
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f"'{self.title}' by {self.author}"
def __repr__(self):
return f"Book(title='{self.title}', author='{self.author}')"
my_book = Book("The Left Hand of Darkness", "Ursula K. Le Guin")
books = [my_book]
print(my_book) # Still calls __str__: 'The Left Hand of Darkness' by Ursula K. Le Guin
print(books) # Now calls __repr__: [Book(title='The Left Hand of Darkness', author='Ursula K. Le Guin')]
Now the debugging process is a breeze. I can see exactly what's inside my list without having to loop through it and print each attribute manually.
A Quick Rule of Thumb
You might wonder: "If I only implement __repr__, do I still need __str__?"
Actually, no. If __str__ is missing, Python falls back to __repr__. But the reverse isn't true. If you only have __str__, you'll still get those ugly hex codes in your lists and logs. My personal rule: always implement __repr__ first. It's your safety net for debugging. Add __str__ only if you need a cleaner, more simplified version for non-developers to see.
📋 Practical Task
Exercise: Debugging the E-Commerce Inventory
You are building an inventory system for a store. You have a Product class, but currently, when you print a list of products, it's impossible to tell which is which because they all look like <__main__.Product object at ...>.
Your Task:
- Implement
__repr__so that it returns a string that looks like the constructor call:Product(name='...', price=...). - Implement
__str__so that it returns a user-friendly string:[Item] Name - $Price.
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
# TODO: Implement __repr__
# TODO: Implement __str__
# Test your code:
p1 = Product("Mechanical Keyboard", 120.00)
p2 = Product("Gaming Mouse", 60.00)
inventory = [p1, p2]
print(f"Single product: {p1}") # Should use __str__
print(f"Inventory list: {inventory}") # Should use __repr__ for items
There are no comments for now.