The struct module packs Python values into bytes and unpacks bytes back into Python values. It is useful for binary files, network packets, C-compatible records, device data, and any format where byte layout matters.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
Quick answer: pack and unpack binary data
Use struct.pack() to convert values to bytes. Use struct.unpack() to convert bytes back to values. Use struct.calcsize() to learn how many bytes a format string needs.
import struct
data = struct.pack("if", 42, 3.14)
print(data)
print(struct.unpack("if", data))
print(struct.calcsize("if"))The first line prints a bytes object. The second returns a tuple such as (42, 3.14). The third prints the byte size of the format, which is 8 on a typical 64-bit Linux system for "if".
Python struct quick reference
| Task | Use |
|---|---|
| Pack values into bytes | struct.pack(format, values...) |
| Unpack bytes into values | struct.unpack(format, data) |
| Get byte size of format | struct.calcsize(format) |
| Pack into existing buffer | struct.pack_into(format, buffer, offset, values...) |
| Unpack from buffer offset | struct.unpack_from(format, buffer, offset=0) |
| Unpack repeated records | struct.iter_unpack(format, buffer) |
| Reuse compiled format | struct.Struct(format) |
| Little-endian data | "<" prefix |
| Big-endian data | ">" prefix |
| Network byte order | "!" prefix |
What is the Python struct module?
struct converts between Python values and binary bytes with a fixed layout. It is not the same as a Python class or dataclass. You define the layout with a format string, and Python reads or writes the exact byte pattern.
import struct
sensor_id = 7
temperature = 23.5
data = struct.pack("<if", sensor_id, temperature)
values = struct.unpack("<if", data)
print(data)
print(values)The first line prints raw bytes such as b'\x07\x00\x00\x00\x00\x00\xbc\x41'. The second prints a tuple like (7, 23.5) with the original values restored.
Common uses include binary file headers, network packets, C-compatible records, sensor or device payloads, and protocol parsing where each field has a known size and order. Pair with Python datetime when timestamps in binary layouts need conversion.
Why use struct in Python?
Normal Python strings hold text. Binary formats need exact byte sizes, byte order, and field boundaries. struct lets you describe that layout in one format string and convert values reliably.
When you read a 4-byte integer from a file or socket, you usually need struct (or a similar binary parser), not string methods.
Python struct format strings
A format string describes how values are stored in bytes. Each character is a type code. Optional prefix characters control byte order, size, and alignment.
Examples:
i— signed integerf— floatd— doubles— bytes (fixed-width byte string)c— one byte character
The Python struct documentation lists every format character and prefix option.
Common struct format characters
| Format | Meaning | Python value |
|---|---|---|
b |
signed char | int |
B |
unsigned char | int |
h |
short | int |
H |
unsigned short | int |
i |
int | int |
I |
unsigned int | int |
q |
long long | int |
Q |
unsigned long long | int |
f |
float | float |
d |
double | float |
? |
bool | bool |
c |
char | bytes of length 1 |
s |
char[] | bytes |
Repeat a character for multiple fields. "iii" packs three integers. "if" packs one integer followed by one float.
Byte order, size, and alignment
If you omit a prefix, struct uses native mode. Native packing depends on your platform and compiler, which is fine for in-process buffers but risky for files and network data.
For external binary formats, be explicit:
<— little-endian>— big-endian!— network byte order (big-endian)
import struct
print(struct.pack(">i", 1))
print(struct.pack("<i", 1))
print(struct.pack("!i", 1))On a typical little-endian Linux host, ">i" and "!i" produce four bytes with the most significant byte first. "<i" reverses the byte order within the 4-byte integer.
struct.pack() example
struct.pack() returns a bytes object. The number of values must match the format string exactly.
import struct
print(struct.pack("i", 42))
print(struct.pack("if", 1, 2.5))
print(struct.pack(">ii", 10, 20))Each call returns bytes. Passing the wrong type, such as a string where an integer is required, raises struct.error.
struct.unpack() example
struct.unpack() converts bytes back into Python values. It always returns a tuple, even for a single value. The buffer size must match struct.calcsize(format).
import struct
data = struct.pack("iii", 1, 2, 3)
print(struct.unpack("iii", data))
print(type(struct.unpack("i", struct.pack("i", 7))))The first unpack returns (1, 2, 3). The second returns a one-item tuple. If the byte length does not match the format, unpack() raises struct.error.
struct.calcsize() example
struct.calcsize() returns how many bytes a format string requires. Use it before reading fixed-size chunks from a file or slicing a buffer.
import struct
print(struct.calcsize("i"))
print(struct.calcsize("iii"))
print(struct.calcsize("if"))For three native integers, calcsize("iii") is typically 12 on a 64-bit Linux system. Always call calcsize() for the exact format you plan to read or write.
pack() and unpack() together
The clearest beginner workflow is a round trip: pack Python values, then unpack the same bytes with the same format.
import struct
fmt = "<if"
values = (100, 3.14)
packed = struct.pack(fmt, *values)
restored = struct.unpack(fmt, packed)
print(packed)
print(restored)You should see bytes on the first line and a tuple matching the original values on the second. The explicit "<" prefix keeps the layout portable across platforms.
Read binary data from a file using struct
Open the file in binary mode ("rb"), read exactly calcsize(format) bytes, then unpack.
import struct
from pathlib import Path
path = Path("record.bin")
fmt = "<if"
raw = path.read_bytes()
if len(raw) != struct.calcsize(fmt):
raise ValueError("unexpected record size")
record_id, value = struct.unpack(fmt, raw)
print(record_id, value)This pattern fits file headers and fixed-width records. For text file patterns, see Python write to file.
Write binary data to a file using struct
Pack values first, then write the bytes. This is binary data, not text, so use "wb" or "ab".
import struct
from pathlib import Path
path = Path("record.bin")
fmt = "<if"
path.write_bytes(struct.pack(fmt, 7, 1.5))The file contains raw bytes. Opening it in a text editor will not show readable text.
struct.pack_into() example
struct.pack_into() writes packed bytes into an existing writable buffer at an offset. A bytearray is the simplest buffer for beginners.
import struct
buf = bytearray(8)
struct.pack_into("ii", buf, 0, 10, 20)
print(bytes(buf))The buffer now holds two native integers. You can also pack at a non-zero offset when the buffer holds a header plus payload.
struct.unpack_from() example
unpack_from() reads from a buffer starting at an offset. The correct name is unpack_from(), not unpack_form(). Use it when data has a header followed by fields.
import struct
header = struct.pack("H", 1)
body = struct.pack("ii", 3, 4)
blob = header + body
offset = struct.calcsize("H")
print(struct.unpack_from("ii", blob, offset))The tuple (3, 4) comes from the body after the 2-byte unsigned short header. The buffer must contain enough bytes from the offset for the full format.
struct.iter_unpack() for repeated records
Use iter_unpack() when a buffer contains many records with the same format. The buffer size must be a multiple of the record size.
import struct
records = struct.pack("ii", 1, 2) + struct.pack("ii", 3, 4)
for item in struct.iter_unpack("ii", records):
print(item)Each iteration yields one tuple. This is useful for binary tables or streams of equal-sized records.
struct.Struct() for reusable formats
When you use the same format many times, compile it once with struct.Struct(format).
import struct
record = struct.Struct("<if")
packed = record.pack(5, 2.5)
print(packed)
print(record.unpack(packed))
print(record.size)A Struct object exposes pack(), unpack(), pack_into(), unpack_from(), iter_unpack(), and size. Compiling once avoids repeating the format string and can be slightly more efficient in tight loops.
Common struct errors
import struct
try:
struct.pack("i", "not-an-int")
except struct.error as exc:
print("wrong type:", exc)
try:
struct.unpack("i", b"\x00\x00")
except struct.error as exc:
print("wrong size:", exc)
try:
struct.pack("b", 300)
except struct.error as exc:
print("out of range:", exc)Typical causes include the wrong number of values, the wrong Python type for a format character, a byte buffer that is too short for unpack(), an integer outside the range of the chosen format, and confusing str with bytes for c or s fields.
Common mistakes with Python struct
- Thinking
structcreates Python objects like C structs. - Forgetting that
pack()returnsbytes, not a string. - Passing normal
strvalues wherebytesare required forcors. - Calling
unpack()with the wrong byte length. - Forgetting that
unpack()always returns a tuple. - Skipping
calcsize()before reading fixed-size records from files. - Relying on native byte order for file or network formats.
- Misspelling
unpack_from()asunpack_form(). - Using
ctypesbuffers when a simplebytearrayis enough forpack_into(). - Not decoding or stripping fixed-width
sfields after unpacking.
Summary
The struct module handles binary data with fixed layouts. pack() converts Python values to bytes. unpack() converts bytes to Python values. calcsize() tells you how many bytes a format requires. Use explicit byte-order prefixes for files and network data. For headers, offsets, repeated records, or hot loops, use pack_into(), unpack_from(), iter_unpack(), or a compiled Struct object.

