Python f-string repr: Using the !r Conversion Flag
Learn how to use the !r conversion flag in Python f-strings to display repr() output, and how to customize __repr__ for better debugging.
In an f-string, Python converts an object to text using its normal string representation; for most objects that is the same as str(). To display the more exact, developer-oriented output produced by repr(), add the !r conversion flag.
The !r Conversion Flag in f-strings
value = "hello" print(f"{value}") # hello print(f"{value!r}") # 'hello'
The first line uses the default string formatting; the second uses repr(). The difference is clear: repr() adds quotes around strings, making the type explicit.
Why repr() Matters for Debugging
str() is meant for end users; repr() is meant for developers. A good repr() should be unambiguous and, where possible, look like valid Python code that could recreate the object. For custom objects, str() may hide details that repr() exposes. For built-in containers, the two forms often look alike:
items = ["apple", "banana", 42] print(f"{items}") # ['apple', 'banana', 42] print(f"{items!r}") # ['apple', 'banana', 42]
Even when str() and repr() produce the same output for a container, they can differ sharply for strings and custom objects.
Using !r with Different Data Types
The !r flag works with any object. Here are a few examples:
from datetime import datetime now = datetime(2024, 3, 15, 10, 30) print(f"{now!r}") # datetime.datetime(2024, 3, 15, 10, 30) class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Point({self.x}, {self.y})" def __str__(self): return f"({self.x}, {self.y})" p = Point(3, 4) print(f"{p}") # (3, 4) print(f"{p!r}") # Point(3, 4)
Notice how repr() for the Point class returns a constructor-like string, which is the recommended pattern.
Customizing repr() in Your Classes
If you want f-strings with !r to be useful for your own classes, define __repr__. A good __repr__ is unambiguous and often includes the class name and the essential attributes.
class InventoryItem: def __init__(self, name, quantity): self.name = name self.quantity = quantity def __repr__(self): return f"InventoryItem(name={self.name!r}, quantity={self.quantity!r})" item = InventoryItem("widget", 3) print(f"{item!r}") # InventoryItem(name='widget', quantity=3)
Using !r inside the __repr__ method itself ensures that string attributes are quoted, making the output more precise.
Common Mistakes and Pitfalls
One common mistake is forgetting the !r flag when you need repr(). This is especially easy when logging or building error messages. Another is using !s when you mean !r; !s converts with str(), so it does not provide the debugging details of repr(). Also, if an object's __repr__ raises an exception, repr() will raise too, though this is uncommon.
Performance and Maintainability Considerations
Calling repr() is generally fast, but if your __repr__ performs expensive computations (e.g., iterating over a large collection), it can slow down logging in hot paths. For maintainability, keep __repr__ simple and deterministic. Avoid relying on repr() for user-facing output; use str() for that.
Advanced Usage: !a and !s Flags
Python f-strings support three conversion flags: !s (str), !r (repr), and !a (ascii). !a is like repr() but escapes non-ASCII characters. This is useful when you need to ensure the output is ASCII-safe.
text = "café" print(f"{text!a}") # 'caf\xe9'
You can also combine conversion flags with format specifiers, though the order matters: conversion first, then format spec.
text = "hello" print(f"{text!r:>10}") # right-aligns the repr string in a width of 10
Here, the repr() conversion runs first, then the format spec right-aligns the resulting string. Do not use numeric specifiers such as .2f with !r; after conversion the value is a string, so those specifiers are invalid.
| Flag | Conversion | Example (value="hello") |
|---|---|---|
!s | str() | hello |
!r | repr() | 'hello' |
!a | ascii() | 'hello' |