How to Convert Bytes to Int in Python
Converting bytes to integers in Python is a fundamental task that comes up frequently when working with binary files, network protocols, low-level system data, or hardware interfaces. Since bytes represent raw binary data, Python needs additional context, such as byte order (endianness) and signedness, to interpret them correctly as numerical values.
In this guide, you will learn the most reliable and commonly used ways to convert bytes into integers in Python, complete with clear explanations, practical examples with outputs, and common pitfalls to avoid.
What Does Converting Bytes to Int Mean?
A bytes object in Python represents a sequence of raw binary data. Converting it to an integer means interpreting that binary sequence as a numeric value. However, the same bytes can produce different integer values depending on how you interpret them.
data = b'\x00\x01'
# Interpreted as big-endian → 1
print(int.from_bytes(data, "big"))
# Interpreted as little-endian → 256
print(int.from_bytes(data, "little"))
Output:
1
256
As you can see, the byte order directly affects the resulting integer. This is why understanding endianness and signedness is essential before performing any conversion.
Big-endian means the most significant byte (MSB) comes first, while little-endian means the least significant byte (LSB) comes first. Network protocols typically use big-endian, whereas many modern CPUs (such as x86) use little-endian internally.
Method 1: Using int.from_bytes() (Recommended)
The built-in int.from_bytes() method is the most Pythonic, readable, and flexible way to convert bytes to integers. It is available in Python 3.2 and later and requires no additional imports.
Syntax
int.from_bytes(bytes, byteorder, *, signed=False)
Parameters
| Parameter | Description |
|---|---|
bytes | A bytes or bytearray object to convert |
byteorder | "big" (MSB first) or "little" (LSB first) |
signed | False (default) for unsigned integers, True for signed integers using two's complement |
Starting with Python 3.11, the byteorder parameter defaults to "big" if omitted, making it optional. In earlier versions, you must always specify it explicitly.
Examples
# Big-endian: most significant byte first
byte_val_1 = b'\x00\x01'
res_1 = int.from_bytes(byte_val_1, "big")
print(f"Big-endian b'\\x00\\x01': {res_1}")
# Output: Big-endian b'\x00\x01': 1
# Little-endian: least significant byte first
byte_val_2 = b'\x00\x10'
res_2 = int.from_bytes(byte_val_2, "little")
print(f"Little-endian b'\\x00\\x10': {res_2}")
# Output: Little-endian b'\x00\x10': 4096
# Signed integer using two's complement
byte_val_3 = b'\xfc\x00'
res_3 = int.from_bytes(byte_val_3, "big", signed=True)
print(f"Signed big-endian b'\\xfc\\x00': {res_3}")
# Output: Signed big-endian b'\xfc\x00': -1024
# Single byte conversion
byte_val_4 = b'\xff'
res_4 = int.from_bytes(byte_val_4, "big")
print(f"Single byte b'\\xff' unsigned: {res_4}")
# Output: Single byte b'\xff' unsigned: 255
# Same single byte, but signed
res_5 = int.from_bytes(byte_val_4, "big", signed=True)
print(f"Single byte b'\\xff' signed: {res_5}")
# Output: Single byte b'\xff' signed: -1
Output:
Big-endian b'\x00\x01': 1
Little-endian b'\x00\x10': 4096
Signed big-endian b'\xfc\x00': -1024
Single byte b'\xff' unsigned: 255
Single byte b'\xff' signed: -1
How the Math Works
Understanding the underlying arithmetic helps verify your conversions:
- Big-endian
b'\x00\x01':0x00 * 256 + 0x01 = 1 - Little-endian
b'\x00\x10':0x00 + 0x10 * 256 = 4096 - Signed
b'\xfc\x00': The raw unsigned value is64512, but two's complement interpretation over 2 bytes yields-1024
Method 2: Using struct.unpack()
The struct module is specifically designed for interpreting bytes as packed binary data. It is the preferred approach when working with binary file formats, network protocols, or any context where data follows a well-defined structure with fixed-size fields.
Syntax
import struct
struct.unpack(format, buffer)
The function returns a tuple, even when unpacking a single value, so you typically access element [0].
Examples
import struct
# 2-byte unsigned short, big-endian
byte_data_1 = b'\x01\x02'
res_1 = struct.unpack('>H', byte_data_1)[0]
print(f"Big-endian unsigned short: {res_1}")
# Output: Big-endian unsigned short: 258
# 4-byte unsigned int, little-endian
byte_data_2 = b'\x01\x00\x00\x00'
res_2 = struct.unpack('<I', byte_data_2)[0]
print(f"Little-endian unsigned int: {res_2}")
# Output: Little-endian unsigned int: 1
# 4-byte signed int, big-endian
byte_data_3 = b'\xff\xff\xff\xfc'
res_3 = struct.unpack('>i', byte_data_3)[0]
print(f"Big-endian signed int: {res_3}")
# Output: Big-endian signed int: -4
# Unpacking multiple values at once
byte_data_4 = b'\x00\x01\x00\x02'
res_4 = struct.unpack('>HH', byte_data_4)
print(f"Two unsigned shorts: {res_4}")
# Output: Two unsigned shorts: (1, 2)
Output:
Big-endian unsigned short: 258
Little-endian unsigned int: 1
Big-endian signed int: -4
Two unsigned shorts: (1, 2)
Format String Reference
| Character | Meaning | Size (bytes) |
|---|---|---|
> | Big-endian byte order | - |
< | Little-endian byte order | - |
! | Network byte order (big-endian) | - |
B | Unsigned char | 1 |
b | Signed char | 1 |
H | Unsigned short | 2 |
h | Signed short | 2 |
I | Unsigned int | 4 |
i | Signed int | 4 |
Q | Unsigned long long | 8 |
q | Signed long long | 8 |
The byte buffer length must exactly match the size expected by the format string. A mismatch will raise a struct.error:
import struct
struct.unpack('>I', b'\x01\x02') # Expects 4 bytes, got 2
# struct.error: unpack requires a buffer of 4 bytes
When to Choose struct.unpack() Over int.from_bytes()
- You need to unpack multiple values from a single byte sequence in one call
- You are parsing structured binary formats (file headers, packet layouts)
- You want the format to serve as self-documenting code for the binary layout
Method 3: Using numpy.frombuffer()
When working with large binary datasets or performance-critical applications, NumPy provides a fast and memory-efficient way to convert bytes to integers. This method truly shines when you need to interpret a byte buffer as an array of integers rather than a single value.
Syntax
import numpy as np
numpy.frombuffer(buffer, dtype)
Examples
import numpy as np
# Big-endian unsigned short (2 bytes)
byte_data_1 = b'\x01\x02'
res_1 = np.frombuffer(byte_data_1, dtype='>u2')[0]
print(f"Big-endian unsigned short: {res_1}")
# Output: Big-endian unsigned short: 258
# Big-endian signed int (4 bytes)
byte_data_2 = b'\x01\x00\x00\x00'
res_2 = np.frombuffer(byte_data_2, dtype='>i4')[0]
print(f"Big-endian signed int: {res_2}")
# Output: Big-endian signed int: 16777216
# Little-endian unsigned int (4 bytes)
byte_data_3 = b'\x01\x00\x00\x00'
res_3 = np.frombuffer(byte_data_3, dtype='<u4')[0]
print(f"Little-endian unsigned int: {res_3}")
# Output: Little-endian unsigned int: 1
# Converting an array of values at once
byte_data_4 = b'\x01\x00\x02\x00\x03\x00\x04\x00'
res_4 = np.frombuffer(byte_data_4, dtype='<u2')
print(f"Array of little-endian unsigned shorts: {res_4}")
# Output: Array of little-endian unsigned shorts: [1 2 3 4]
Output:
Big-endian unsigned short: 258
Big-endian signed int: 16777216
Little-endian unsigned int: 1
Array of little-endian unsigned shorts: [1 2 3 4]
NumPy dtype Notation
| Prefix | Meaning |
|---|---|
> | Big-endian |
< | Little-endian |
u | Unsigned integer |
i | Signed integer |
| Number | Size in bytes (1, 2, 4, or 8) |
For example, <u4 means "little-endian unsigned 4-byte integer."
When to Use NumPy
- Processing large binary streams with many values
- Working with scientific or sensor data stored in binary format
- Situations where vectorized operations on the resulting integers are needed
- Performance-critical applications where converting thousands or millions of values one by one would be too slow
NumPy is a third-party library and must be installed separately (pip install numpy). For simple, single-value conversions, int.from_bytes() or struct.unpack() are more appropriate since they require no external dependencies.
Common Mistake: Using the Wrong Byte Order
One of the most frequent bugs when converting bytes to integers is assuming the wrong endianness. The same bytes produce completely different values depending on byte order.
Incorrect assumption (expected 1, used big-endian):
result = int.from_bytes(b'\x01\x00', "big")
print(result)
Output:
256
Correct approach (data is actually little-endian):
result = int.from_bytes(b'\x01\x00', "little")
print(result)
Output:
1
Always check the documentation of your data source to confirm its endianness. Network data typically uses big-endian (also called "network byte order"), while data from x86-based systems is usually little-endian. You can also check your system's native byte order with sys.byteorder.
Common Mistake: Forgetting to Handle Signed Integers
When bytes represent a signed value (such as a negative temperature reading or a signed offset), omitting the signed=True parameter produces an incorrect positive number.
Incorrect (treats b'\xff\xff' as unsigned):
result = int.from_bytes(b'\xff\xff', "big")
print(result)
Output:
65535
Correct (interprets as signed using two's complement):
result = int.from_bytes(b'\xff\xff', "big", signed=True)
print(result)
Output:
-1
If you are using struct.unpack(), choose the correct format character: use lowercase letters (b, h, i, q) for signed types and uppercase letters (B, H, I, Q) for unsigned types.
Quick Comparison of All Three Methods
| Feature | int.from_bytes() | struct.unpack() | numpy.frombuffer() |
|---|---|---|---|
| Built-in | Yes | Yes | No (requires NumPy) |
| Handles arbitrary byte lengths | Yes | No (fixed sizes only) | No (fixed sizes only) |
| Unpack multiple values at once | No | Yes | Yes |
| Best for bulk data | No | No | Yes |
| Readability | High | Medium | Medium |
| Recommended for general use | Yes | Structured formats | Large datasets |
Best Practices
- Prefer
int.from_bytes()for most single-value conversions due to its clarity and simplicity. - Use
struct.unpack()when dealing with structured binary formats or when you need to unpack multiple fields from a single buffer. - Use NumPy for large-scale binary data processing where performance matters.
- Always verify byte order and signedness against the specification of your data source before performing any conversion.
- Document binary formats clearly in your codebase so other developers understand the expected byte layout.
- Validate input length before conversion to avoid unexpected errors from truncated or malformed data.
Conclusion
Python provides multiple powerful tools to convert bytes into integers. For most everyday applications, int.from_bytes() is the best choice, offering excellent readability and full control over byte order and signedness. For parsing structured binary data with multiple fields, struct.unpack() is the natural fit. When performance is critical and you are processing large volumes of binary data, NumPy delivers the speed and efficiency you need.
Understanding how bytes map to integers, and being mindful of endianness and signedness, ensures correctness, portability, and reliability whenever you work with low-level binary data in Python.