Python bytes.fromhex(): Convert Hex Strings to Bytes
python bytes fromhex: Use Python's bytes.fromhex() method to convert hexadecimal strings into bytes objects, including whitespace handling, ValueError cases, and perfo...
bytes.fromhex() is the Python class method that turns a hexadecimal string into a bytes object. It's the direct answer to the common python bytes fromhex conversion task: given text like "48656c6c6f", it returns b'Hello'.
>>> bytes.fromhex("48656c6c6f") b'Hello'
The method reads pairs of hex digits and produces one byte per pair. 48 becomes H, 65 becomes e, and so on. The input is a regular str, and the result is always a new bytes object.
How bytes.fromhex() Parses Its Input
The parser is lenient about formatting in one specific way: it ignores ASCII whitespace between hex digits. That includes spaces, tabs, and newlines.
>>> bytes.fromhex("48 65 6c 6c 6f") b'Hello' >>> bytes.fromhex("48\n65\t6c 6c 6f") b'Hello'
Both examples produce the same result as the single continuous string. This makes the method convenient for parsing formatted hex dumps, where bytes are often separated by spaces for readability.
Hex digits may be uppercase or lowercase. "48656c6c6f" and "48656C6C6F" are equivalent.
The parser does not accept a 0x prefix. bytes.fromhex("0x48") raises ValueError. If your input uses that prefix, strip it before calling the method.
Return Value and the bytes vs bytearray Distinction
bytes.fromhex() returns an immutable bytes object. The sibling class method bytearray.fromhex() behaves identically but returns a mutable bytearray.
>>> bytes.fromhex("48656c6c6f") b'Hello' >>> bytearray.fromhex("48656c6c6f") bytearray(b'Hello')
Choose bytes when the result will be used as a key, stored in a set, or passed to an API that requires immutable input. Choose bytearray when you need to modify the binary data in place after conversion.
Error Handling: When fromhex() Raises ValueError
bytes.fromhex() raises ValueError in two cases:
- The input contains a character that is not a hex digit or whitespace.
- The number of hex digits is odd, so the final digit has no pair.
>>> bytes.fromhex("48656c6c6") # odd number of digits ValueError >>> bytes.fromhex("48 6g 6c 6c") # 'g' is not a hex digit ValueError
The error is raised eagerly, meaning the entire input is validated before any result is returned. You do not get a partially converted bytes object.
If you're parsing untrusted input, catch ValueError explicitly rather than letting it propagate as an unhandled exception.
def parse_hex(data: str) -> bytes | None: try: return bytes.fromhex(data) except ValueError: return None
Practical Use Cases
The most common use of bytes.fromhex() is converting hex-encoded data from text-based formats into binary form. Network protocols, configuration files, and log dumps frequently represent binary payloads as hex strings.
A typical example is parsing a MAC address:
mac = "a4:5e:60:b7:9e:21" mac_bytes = bytes.fromhex(mac.replace(":", ""))
Another common case is reading a hex dump from a file and converting it before writing it back out as binary:
with open("dump.hex") as f: hex_data = f.read().strip() binary = bytes.fromhex(hex_data)
Performance and Memory Behavior
bytes.fromhex() runs in linear time relative to the length of the input. Each pair of hex digits requires a single byte of output, so the result is always half the length of the hex string (after whitespace is removed).
The method allocates a new bytes object every time it is called. For large inputs, that allocation is the dominant cost. If you convert the same constant hex string repeatedly, store the result once rather than calling fromhex() in a loop.
Whitespace handling has a small cost: the parser must skip non-hex characters while scanning. For typical inputs this is negligible, but if you control the data format, feeding a compact hex string without spaces avoids that overhead entirely.
Comparing bytes.fromhex() with Other Conversion Paths
Several other APIs can convert hex text to binary data. The right choice depends on your input format and what you need the result for.
| Conversion path | Input type | Result | Ignores whitespace | Accepts 0x prefix |
|---|---|---|---|---|
bytes.fromhex(s) | str | bytes | Yes | No |
bytearray.fromhex(s) | str | bytearray | Yes | No |
binascii.unhexlify(s) | str or bytes | bytes | No | No |
int(s, 16) | str | int | No | Yes |
binascii.unhexlify is faster for large inputs because it does no whitespace processing, but it rejects any space in the input. int(s, 16) is useful when you only need a numeric value, not a byte sequence, and it accepts the 0x prefix.
For most purposes, bytes.fromhex() is the most convenient choice because its whitespace tolerance handles formatted input without preprocessing.
Edge Cases and Limitations
An empty string is valid and produces an empty bytes object:
>>> bytes.fromhex("") b''
Whitespace-only input also produces an empty result, since all characters are ignored:
>>> bytes.fromhex(" ") b''
The method does not accept 0x prefixes, so strip them first:
>>> bytes.fromhex("0x48656c6c6f".removeprefix("0x")) b'Hello'
The input must be a str. Passing bytes raises a TypeError, since the method expects a text sequence of hex digits.
For large binary payloads, consider whether you need the data in memory as a single bytes object at all. If you're streaming data, processing chunks individually avoids holding the entire decoded payload at once.