__str__ versus __unicode__
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The __str__ versus __unicode__ question is mostly a Python 2 compatibility topic. In modern Python 3 code, __unicode__ does not exist as part of the data model, and __str__ should return normal Unicode text. The confusion comes from old Python 2 code, where byte strings and Unicode strings were different types and object display methods had to account for both.
How It Worked In Python 2
Python 2 had two important text-like types:
- '
strfor byte strings' - '
unicodefor Unicode text'
Because of that split, classes sometimes implemented both __str__ and __unicode__.
A legacy Python 2 style class might look like this:
In that world:
- '
unicode(obj)called__unicode__' - '
str(obj)expected a byte string from__str__'
That design existed because Python 2 could not treat all text as one unified Unicode type.
How It Works In Python 3
Python 3 simplified the model. str is Unicode text, so __str__ should return a str object and that is usually all you need.
There is no separate __unicode__ hook to implement in normal Python 3 code.
If you need a byte representation in Python 3, the relevant method is __bytes__, not __unicode__.
Where __repr__ Fits In
This topic is often easier to understand when you place __repr__ beside __str__.
- '
__str__is the readable, user-facing representation' - '
__repr__is the developer-facing representation used for debugging when possible'
In modern code, that pairing matters much more than __str__ versus __unicode__.
Porting Old Code Safely
When migrating Python 2 code, the safest mental model is to remove the old byte-versus-Unicode split from your object methods. Keep text as Unicode internally and make __str__ return that text directly.
If legacy code used __unicode__ as the source of truth, you can usually fold that logic into __str__ during the port and delete the older method entirely. That tends to simplify the class and remove a whole category of encoding mistakes.
The only time you still need special handling is when code must emit raw bytes for a protocol or file format, and that belongs in __bytes__ or an explicit encoding step, not in __str__.
What To Do In Real Code Today
If you are writing Python 3, implement __str__ when you want a readable string form. Ignore __unicode__ entirely unless you are maintaining Python 2 compatibility code, which is rare now.
If you are maintaining old Python 2 code, the common historical pattern was:
- put the real text logic in
__unicode__ - have
__str__return encoded bytes derived from it
But that pattern should stay in legacy maintenance, not in new Python 3 code.
Common Pitfalls
The most common mistake is trying to implement __unicode__ in Python 3 and expecting it to be called. It will not be part of normal string conversion.
Another issue is returning bytes from __str__ in Python 3. __str__ must return a text str, not bytes.
Developers also sometimes use __str__ for debugging-heavy output when __repr__ would be more appropriate. The two methods serve different audiences.
Finally, when porting Python 2 code, test every place that used implicit string conversion. Old byte-oriented assumptions often break during migration.
Summary
- '
__unicode__is mainly a Python 2 concept tied to the oldunicodetype.' - In Python 3, implement
__str__for readable Unicode text output. - Use
__bytes__if you need a byte representation in modern code. - '
__repr__is usually the more relevant companion to__str__today.' - Do not carry Python 2 string patterns into new Python 3 code unless you truly maintain legacy compatibility.

