bytes.fromhex()

The inverse of bytes.hex. Unlike float.fromhex there is no 0x prefix and no ambiguity — but the digits must come in complete pairs.

Bytes classmethodPython 3.0+Live demo
Common call
bytes.fromhex('616263')
Returns
bytes — one byte per pair of digits
Replaces
binascii.unhexlify, which is stricter about spaces
Watch out
an odd number of digits raises; spaces between bytes are fine
bytes.fromhex(stringstring — Hex digits in pairs. ASCII spaces between bytes are skipped. Upper and lower case both work. No 0x prefix is permitted.type: str · required)
→ bytes

Demo

Live evaluation
Try:
Inputs
sstrhex digits, e.g. 616263
Output
bytes.fromhex('616263')
b'abc'

Each pair of hex digits becomes one byte, so 616263 gives b'abc'. Spaces between bytes are skipped, which means output from hex(' ') parses straight back. The two failure cases are worth reading closely, because the reported POSITION differs: "zz" fails at position 0, where the first bad character sits, while "616" fails at position 3 — the index where the missing fourth digit should have been.

Parameters

NameTypeRequiredDescription
stringstryesHex digits in pairs. ASCII spaces between bytes are skipped. Upper and lower case both work. No 0x prefix is permitted.

Return value

bytes — The bytes the hex digits represent. Raises ValueError, naming the exact position, for anything that is not a hex digit or a space.

Common patterns

Round-trip binary through text
The exact inverse of hex, tolerant of the separator form.
data = bytes.fromhex(text)
assert data.hex() == text.replace(' ', '')
Binary literals in test fixtures
Far easier to read and edit than a long escaped bytes literal.
PACKET = bytes.fromhex('deadbeef')
Read a key or digest from config
Hex is the usual text form for secrets and hashes.
key = bytes.fromhex(os.environ['KEY_HEX'])

Examples

1. Plain digits
bytes.fromhex('616263')
Returns
b'abc'
2. Spaces allowed
bytes.fromhex('61 62 63')
Returns
b'abc'
3. Case-insensitive
bytes.fromhex('DEADBEEF')
Returns
b'\xde\xad\xbe\xef'
4. Empty
bytes.fromhex('')
Returns
b''
5. Odd length
bytes.fromhex('616')
Returns
ValueError: ... at position 3
6. Round trip
bytes.fromhex(b'abc'.hex())
Returns
b'abc'

Pitfalls

1. An odd number of digits raises
Every byte needs exactly two digits, so a trailing lone digit is an error rather than an implied leading zero. Truncated input therefore fails loudly, which is the right behaviour but catches people slicing hex strings by hand.
Incomplete pair
bytes.fromhex('616')
ValueError: non-hexadecimal number found in fromhex() arg at position 3
Pad it
bytes.fromhex('0616')
b'\x06\x16'
2. No 0x prefix allowed
Unlike float.fromhex, a prefix is a hard error — x is not a hex digit. Copying a value from a debugger or a C literal usually means stripping the prefix first.
Prefix rejected
bytes.fromhex('0x616263')
ValueError: non-hexadecimal number found in fromhex() arg at position 1
Strip it
bytes.fromhex('0x616263'.removeprefix('0x'))
b'abc'
3. Only ASCII spaces are skipped
Spaces between bytes are fine, but colons, dashes and newlines are not. MAC addresses and colon-separated dumps need the separators removed first.
Colons rejected
bytes.fromhex('61:62:63')
ValueError: non-hexadecimal number found in fromhex() arg at position 2
Replace first
bytes.fromhex('61:62:63'.replace(':', ''))
b'abc'
4. It is a classmethod
Call it on the type — bytes.fromhex(s). There is no bare fromhex function, and bytearray has its own version producing a mutable result.
No such builtin
fromhex('616263')
NameError: name 'fromhex' is not defined
Call on the type
bytes.fromhex('616263')
b'abc'

When to use

Use it
  • Reading back data written with bytes.hex
  • Binary literals in tests, where hex beats escapes for readability
  • Keys, digests and checksums arriving as text
Reach for something else
  • Base64 input → base64.b64decode
  • Separators other than spaces → strip them first
  • The text is really text → encode it instead

Notes

Complexity
O(n) — a single pass over the digits
Return
A new bytes object, half the length of the digit count
CPython impl
Objects/bytesobject.c :: bytes_fromhex
Memory
Allocates a buffer half the size of the input string
Thread-safe
Yes — bytes and str are immutable

FAQ

Because position 3 is where the missing digit should have been. Python reports where parsing ran out, not where the last good pair ended — which makes the message point at what is absent rather than what is present.

bytes.fromhex('616')
# ValueError: ... at position 3

History

3.0
bytes.fromhex added with the bytes type.
3.7
Skipping of ASCII spaces extended to all whitespace positions between bytes.