What's the correct way to convert bytes to a hex string in Python 3?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Python 3, the cleanest way to convert a bytes object to hexadecimal text is to use the built-in .hex() method. It is readable, efficient, and part of the standard bytes API. Older approaches using binascii still work, but they are usually only necessary when you are interoperating with older code.
Use the built-in .hex() method
For ordinary Python 3 code, this is the standard answer:
Output:
The result is a normal Python string containing lowercase hexadecimal digits. Every byte becomes two hex characters.
When you need uppercase output for display or protocol formatting, convert the resulting string afterward with .upper() rather than building a custom conversion loop. That keeps the actual byte-to-hex conversion simple.
This method also works on bytearray objects:
When you want separators
Python's .hex() method also supports a separator argument, which is useful for debugging or display.
Output:
This is much cleaner than manually joining formatted bytes in a loop.
Converting back from hex text
If you later need to turn the hex string back into bytes, use bytes.fromhex().
That makes .hex() and bytes.fromhex() a useful pair for serialization and debugging.
The older binascii approach
Before .hex() became the natural choice, many codebases used binascii.hexlify().
This still works, but it is more verbose because hexlify returns bytes rather than a Python string. In new Python 3 code, data.hex() is usually clearer.
Avoid common confusion around strings and bytes
A frequent source of bugs is forgetting the difference between a text string and a bytes object.
This works because encoded is bytes. By contrast, a plain Python string does not have a .hex() method for this purpose.
So if your input starts as text, encode it first. If it is already bytes, convert it directly.
When hex is useful
Hexadecimal representations are especially useful for:
If you need a prefixed display such as 0xdeadbeef, add that prefix separately in string formatting rather than expecting .hex() to include it automatically. The method returns only the hexadecimal digits.
- debugging binary protocols
- logging hashes and binary identifiers
- inspecting encrypted or compressed payloads
- generating human-readable representations of raw bytes
Hex is much easier to inspect than printing raw bytes directly, especially when the data includes non-printable values.
Common Pitfalls
The biggest pitfall is calling .hex() on a text string instead of on bytes. Convert text to bytes first if needed.
Another issue is forgetting that the result is a string, not bytes. If a downstream API expects raw bytes, use the original bytes object or convert the hex text back with bytes.fromhex().
It is also easy to reach for binascii.hexlify() out of habit when .hex() is already available and simpler.
Finally, remember that hex doubles the visible length: one byte becomes two hex characters. That matters when you compare lengths or transmit encoded values.
Summary
- In Python 3, the preferred way to convert bytes to hex text is
data.hex(). - Use the separator form of
.hex()when you want readable debug output. - Use
bytes.fromhex()to convert hex text back into bytes. - Older
binascii.hexlify()code still works but is usually more verbose. - Make sure you know whether your value is text or bytes before converting it.

