Tutorial

Python struct pack, unpack

Clear guide to python struct pack unpack: how to pack values into bytes, unpack into tuples, calcsize, pack_into/unpack_from, endianness, and practical tips.

Drake Nguyen

Founder · System Architect

3 min read
Python struct pack, unpack
Python struct pack, unpack

Overview: python struct pack unpack

The Python struct module provides tools to convert between Python values and C-style binary representations. When you need to read or write binary files, communicate over sockets, or interoperate with C libraries, using struct.pack and struct.unpack lets you pack values into bytes and recover them later with a matching struct format string.

When to use the struct module

  • Serializing numeric values and fixed-layout records into python binary data for files or network transmission.
  • Interpreting byte buffers produced by C programs or network protocols that follow a specific layout.
  • Working with low-level byte buffers, memory views, or ctypes-created buffers for performance-sensitive code.

Core functions: pack, unpack and friends

  • struct.pack(format, ...) — pack Python values into a bytes object according to the format string.
  • struct.unpack(format, bytes) — unpack a bytes buffer into a tuple of Python values; note that unpack always returns a tuple.
  • struct.calcsize(format) — compute how many bytes a given format occupies (useful to validate buffers).
  • struct.pack_into(format, buffer, offset, ...) — write packed values directly into a writable buffer (like a bytearray or ctypes buffer).
  • struct.unpack_from(format, buffer, offset=0) — extract values from a buffer without slicing, returning a tuple.

Format string basics

A struct format string describes the types and order of fields. It can also specify byte order and alignment with a prefix:

  • @ — native order, native alignment (default)
  • = — native order, standard sizes, no alignment
  • < — little-endian, standard sizes
  • > — big-endian, standard sizes
  • ! — network (= big-endian) byte order

Common format characters (cheat sheet)

?   boolean (1 byte)
# not allowed in code - comment line
b   signed char (1 byte)
B   unsigned char (1 byte)
h   short (2 bytes)
H   unsigned short (2 bytes)
i   int (4 bytes)
I   unsigned int (4 bytes)
q   long long (8 bytes)
Q   unsigned long long (8 bytes)
f   float (4 bytes)
d   double (8 bytes)
s   char[] (string of specified length)
p   pascal string

Examples: python struct pack and unpack example

Basic pack/unpack workflow — pack values into bytes and read them back with the same format:

import struct

# pack three signed integers into bytes
packed = struct.pack('iii', 10, 20, 30)
print(type(packed), packed)  # bytes object

# unpack returns a tuple of values
values = struct.unpack('iii', packed)
print(values)  # (10, 20, 30)

Remember: the format must match the data layout exactly. If the buffer length does not match the expected size, unpack will raise an exception.

Using calcsize to verify sizes

import struct
size = struct.calcsize('hhl')  # number of bytes for short, short, long
print('bytes required:', size)

Packing into and unpacking from buffers

If you need to operate on an existing buffer without allocating new bytes, use pack_into and unpack_from. These are handy for building binary messages in a pre-allocated buffer.

import struct
import ctypes

fmt = 'hhl'
size = struct.calcsize(fmt)
buf = ctypes.create_string_buffer(size)

# write values directly into the buffer
struct.pack_into(fmt, buf, 0, 5, 10, 15)

# read values back without slicing
result = struct.unpack_from(fmt, buf, 0)
print(result)  # tuple

Endianness: little vs big (network byte order)

Byte order matters when exchanging binary data across machines. Use < for little-endian and > or ! for big-endian (network byte order). For example, when sending integers over a socket, prefer '!I' to ensure consistent interpretation.

# big-endian unsigned int
packed_be = struct.pack('!I', 0x12345678)
# little-endian unsigned int
packed_le = struct.pack('

Practical tips and pitfalls

  • Always use calcsize or check len(bytes) before unpacking to avoid exceptions.
  • Be explicit about endianness in protocols — Netalith not rely on native defaults when communicating between systems.
  • Note that alignment and padding affect size when using native format (@); use standard sizes (</>/! or =) for portable layouts.
  • For large binary work, consider memoryview, bytearray, or ctypes buffers with pack_into to reduce allocations.

Use struct.pack to create compact byte sequences and struct.unpack to recover typed values. When in doubt, include explicit format prefixes and verify with calcsize.

Stay updated with Netalith

Get coding resources, product updates, and special offers directly in your inbox.