Python str Conversion: str(), repr(), and Formatting
python str conversion: Learn Python str conversion with str(), repr(), f-strings, and format(). Control how objects become strings in your code.
Converting a Python object to a string is a routine operation, yet the choice of method changes the result. The built-in str() and repr() functions serve different purposes, and f-strings or format() give you explicit control over the output. Understanding these tools is essential for logging, user-facing messages, serialization, and debugging. This article covers the mechanics of python str conversion, when to use each approach, and how to customize the behavior for your own classes.
The Two Built-in Conversion Functions: str() and repr()
Python provides two built-in functions for converting objects to strings: str() and repr(). Both accept an object and return a string, but they are designed for different audiences.
str() is meant to produce a readable, human-friendly representation. repr() is meant to produce an unambiguous representation that, ideally, could be used to recreate the object. For many built-in types, the difference is clear:
value = 42 print(str(value)) # '42' print(repr(value)) # '42' text = "hello" print(str(text)) # 'hello' print(repr(text)) # "'hello'"
For strings, repr() adds quotes, making the type explicit. For numbers, both return the same digits. The distinction becomes more important with custom objects, where repr() often includes the class name and key attributes.
When you call print() on an object, Python uses str() internally. When you inspect an object in the interactive interpreter, it uses repr(). The __str__ and __repr__ methods on a class define these behaviors.
F-strings and format() for Controlled Conversion
F-strings, introduced in Python 3.6, are the most direct way to embed expressions inside string literals. They call str() on each interpolated value by default, but you can apply format specifiers to control alignment, width, precision, and more.
price = 19.995 print(f"{price:.2f}") # '20.00' print(f"{price:>10.2f}") # ' 20.00' name = "Ada" print(f"{name:<10}") # 'Ada '
The format() method on strings offers the same formatting capabilities without the f-string syntax, which is useful when the format string is dynamic:
template = "{name}: {score:.1f}" print(template.format(name="Bob", score=9.75))
Both f-strings and format() rely on the __format__ method of the object. By default, __format__ delegates to __str__ when no specifier is given, but you can override it to handle custom formatting.
Customizing Conversion with str and repr
Defining __str__ and __repr__ on your classes gives you precise control over how instances are converted to strings. The __str__ method should return a readable description, while __repr__ should be unambiguous and, where practical, include enough information to reconstruct the object.
class Point: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"({self.x}, {self.y})" def __repr__(self): return f"Point({self.x!r}, {self.y!r})" p = Point(3, 4) print(str(p)) # '(3, 4)' print(repr(p)) # 'Point(3, 4)'
Notice the use of !r inside the f-string in __repr__. That conversion flag forces repr() on the attribute, which is a common pattern to preserve exactness. Without __str__, Python falls back to __repr__ for str() calls, so implementing __repr__ alone is often sufficient for debugging.
Converting Collections and Nested Objects
When you convert a list, dict, or tuple to a string, Python recursively applies repr() to the contained elements, not str(). This behavior is important when you have custom objects inside a collection.
points = [Point(1, 2), Point(3, 4)] print(str(points)) # '[Point(1, 2), Point(3, 4)]'
The list's __str__ method uses repr() on each item. If you want a different representation, you must build the string manually, often with a list comprehension and str():
print("[" + ", ".join(str(p) for p in points) + "]") # '[(1, 2), (3, 4)]'
For nested data structures, this distinction can cause surprising output if your __str__ and __repr__ differ significantly. Always test how your objects appear inside containers, especially when logging or serializing data.
Performance and Runtime Considerations
String conversion is not free. Each call to str() or repr() allocates a new string object, and for large or deeply nested structures, the conversion can be expensive. In performance-sensitive code, such as tight loops or high-frequency logging, unnecessary conversions add measurable overhead.
Consider a loop that builds a log message only when debugging is enabled:
if debug_enabled: log(f"Current state: {state}")
If debug_enabled is false, the f-string is never evaluated, so no conversion happens. But if you pre-convert the object outside the conditional, you pay the cost regardless:
state_str = str(state) # wasted work if debug is off if debug_enabled: log(f"Current state: {state_str}")
Lazy formatting is a common pattern in logging libraries for this reason. Also, be aware that repr() on a large list of objects can be significantly slower than str() on each element, because it creates a full representation of the entire structure. For very large collections, consider generating a summary string manually.
Common Pitfalls and Compatibility Notes
One frequent mistake is assuming str() and repr() are interchangeable. They are not. Using str() on a bytes object gives you "b'...'", while repr() gives the same but with quotes. For user-facing output, str() is usually correct; for debugging, repr() is safer.
Another issue is handling None. Both str(None) and repr(None) return 'None', which is often what you want. But if you are building a string from a value that might be None, consider using a default:
value = None print(f"{value!r}") # 'None'
Python 2 had a different string model, but modern Python 3 code should not rely on unicode or basestring. If you are maintaining legacy code, be aware that str() on a bytes object in Python 3 returns the literal "b'...'" representation, not the decoded text. Use .decode() when you need the actual characters.
Choosing the Right Conversion Approach
The right conversion method depends on your goal. For user-facing messages, use str() or f-strings with format specifiers to control presentation. For debugging and logging, use repr() to get unambiguous output that includes type information. When you need a machine-readable format, consider json.dumps() or pickle instead of manual string conversion.
For custom classes, implement both __str__ and __repr__ with distinct purposes. Use __repr__ to provide a developer-friendly string that can often be copied back into code. Use __str__ to provide a concise, human-readable version. If you only implement one, __repr__ is the better choice because it serves as a fallback for str().
Finally, remember that f-strings and format() give you the most control. They allow you to pad, align, and format numbers without writing manual concatenation. When you need to convert an object to a string in a specific format, prefer these over repeated str() calls.