What Is the Python Array Module (and Why Not Just Use a List)?

The python array module is a part of Python's standard library that gives you a typed, memory-compact sequence for storing homogeneous numeric data. If you have used Python lists before, you know they accept anything — integers, strings, floats, even other lists all mixed together. That flexibility is powerful, but it comes with overhead. Every item in a list is a full Python object carrying its own type metadata and reference count.

The python array module works differently. You declare a single type upfront, and every element is stored as a raw C value back to back in memory — no object wrappers, no type tags per element. The result is a sequence that behaves like a list for most operations but uses a fraction of the memory when you are working with thousands of numbers.

There is nothing to install. The module ships with every Python distribution:

import array

That single line is all you need. Everything in the module revolves around one class — array.array — which you will use to create every typed array in your programs.

Python Array Typecodes: Picking the Right Data Type

Before you can create a python array, you need to choose a typecode. A python array typecode is a single character that tells the array what C type to use for each element and how many bytes each element occupies. This is the key distinction from a plain list — once you set a typecode, every element you add must match it.

Here are the most common typecodes from the official Python array documentation:

TypecodeC TypePython TypeBytes
'b'signed charint1
'B'unsigned charint1
'h'signed shortint2
'H'unsigned shortint2
'i'signed intint2
'l'signed longint4
'L'unsigned longint4
'f'floatfloat4
'd'doublefloat8

For everyday whole numbers, 'i' is the go-to choice. For decimal values where precision matters, reach for 'd' (double) over 'f' (float). If you are working with raw byte data like image pixels or audio samples, 'B' (unsigned char, 0–255) is exactly what you need.

When you try to add a value that does not match the declared typecode — say, a string into an 'i' array — Python raises a TypeError right at the point of insertion. That immediate feedback is a feature, not a limitation. It keeps your data clean and saves you from silent bugs that only appear much later.

Creating Your First Python Array

The array.array() constructor takes two arguments: the typecode string and an optional iterable of initial values.

import array

# Create an integer array holding daily step counts
steps = array.array('i', [8432, 11205, 7890, 9644, 12300])
print(steps)
print("Typecode:", steps.typecode)
print("Bytes per item:", steps.itemsize)
print("Total elements:", len(steps))
array('i', [8432, 11205, 7890, 9644, 12300])
Typecode: i
Bytes per item: 2
Total elements: 5

When you print a python array, the output always shows the typecode alongside the values. This makes it immediately clear what kind of data you are dealing with. The itemsize attribute tells you the exact byte width of each element — useful when you are calculating buffer sizes or writing data to disk.

You can also start with an empty array and populate it later:

import array

readings = array.array('d')
print("Empty array:", readings)
print("Length:", len(readings))
Empty array: array('d')
Length: 0

Core Array Module Functions You Will Actually Use

The array module functions closely mirror what Python lists offer, with the added constraint that every value must match the declared type. Let us walk through each one with real examples.

append

append() adds a single element to the end of the array. It works exactly like list.append but enforces the typecode.

import array

voltages = array.array('f', [3.3, 5.0, 1.8])
voltages.append(12.0)
print(voltages)
array('f', [3.2999999523162842, 5.0, 1.7999999523162842, 12.0])

The slight rounding on 3.3 and 1.8 is normal floating-point behavior — 'f' is a 4-byte float with limited decimal precision. Use 'd' (8-byte double) when you need more accuracy.

extend

extend() takes any iterable and appends all of its elements at once. Think of it as bulk-appending without a loop.

import array

frame_ids = array.array('i', [100, 101, 102])
frame_ids.extend([103, 104, 105])
print(frame_ids)
array('i', [100, 101, 102, 103, 104, 105])

insert

insert(index, value) places a new value at a specific position, shifting everything after it one step to the right.

import array

lap_times = array.array('i', [58, 61, 63, 67])
lap_times.insert(2, 60) # Insert 60 at position 2
print(lap_times)
array('i', [58, 61, 60, 63, 67])

remove and pop

remove(value) deletes the first occurrence of a matching value. pop(index) removes and returns the element at a given index — defaulting to the last element when no index is given.

import array

codes = array.array('i', [200, 404, 301, 404, 500])
codes.remove(404) # Removes first 404
print("After remove:", codes)

returned = codes.pop(1) # Removes element at index 1
print("Popped value:", returned)
print("After pop:", codes)
After remove: array('i', [200, 301, 404, 500])
Popped value: 301
After pop: array('i', [200, 404, 500])

reverse

reverse() flips the array in place. No new object is created — the existing array is modified directly.

import array

scores = array.array('i', [42, 87, 55, 91, 33])
scores.reverse()
print(scores)
array('i', [33, 91, 55, 87, 42])

Python Array fromlist: Loading Data in Bulk

fromlist() is one of the most practical array module functions for real-world code. It reads an entire Python list and appends all of its values to an existing python array in one call — no loop required.

import array

sensor_log = array.array('i')
burst_data = [512, 498, 523, 507, 511, 489, 530]
sensor_log.fromlist(burst_data)
print(sensor_log)
array('i', [512, 498, 523, 507, 511, 489, 530])

The reverse operation — tolist() — converts a python typed array back into a regular Python list. This is essential when you need to pass your array data to a function or library that only accepts lists.

import array

coefficients = array.array('d', [0.25, 0.50, 0.75, 1.00])
plain_list = coefficients.tolist()
print(plain_list)
print(type(plain_list))
[0.25, 0.5, 0.75, 1.0]
<class 'list'>

The fromlist() and tolist() pair gives you a clean gateway between the typed world of the python array module and the flexible world of standard Python lists without having to rewrite any surrounding logic.

Python Array Buffer: Working with Raw Bytes

One of the more powerful features of the python array module is its ability to work directly with raw memory. The tobytes() method serializes your entire array into a flat bytes object — each element written as its underlying C type, back to back, with no padding or wrapper overhead. This is the python array buffer representation, and it is exactly what you would write to a binary file or send over a network socket.

import array

pixel_row = array.array('B', [255, 128, 64, 0, 192, 32])
raw = pixel_row.tobytes()
print("Raw bytes:", raw)
print("Byte length:", len(raw))
Raw bytes: b'\xff\x80\x40\x00\xc0\x20'
Byte length: 6

Each unsigned byte element ('B') takes exactly one byte, so six elements produce six bytes. For an 'i' array (2 bytes per element), six elements would produce twelve bytes. The byte count is always len(array) * array.itemsize.

frombytes() goes in the other direction — it reads a raw bytes object and appends the decoded values to an existing array. Together, tobytes() and frombytes() give you a complete binary round-trip.

import array

restored = array.array('B')
restored.frombytes(b'\xff\x80\x40\x00\xc0\x20')
print(restored)
array('B', [255, 128, 64, 0, 192, 32])

The python array buffer interface also integrates with Python's built-in memoryview, which lets you pass a zero-copy view of the array's raw memory directly to libraries like struct, socket, and io without any intermediate copying.

Python Array vs List: Memory and Type Safety

The most common question beginners have is: when should I use a python array vs list? The practical answer comes down to two things — memory footprint and type enforcement.

To see the difference concretely, compare how much space a list and a python array module array use when storing the same thousand integers:

import array
import sys

big_list = list(range(1000))
big_array = array.array('i', range(1000))

raw_array_bytes = big_array.buffer_info()[1] * big_array.itemsize
list_object_bytes = sys.getsizeof(big_list)

print(f"Array raw payload: {raw_array_bytes} bytes")
print(f"List object header: {list_object_bytes} bytes")
Array raw payload: 2000 bytes
List object header: 8056 bytes

The list figure only counts the list object itself — each of the 1000 Python integer objects it references carries additional memory on top of that. The python typed array stores raw 2-byte integers end to end, totaling exactly 2000 bytes for 1000 elements with no per-element overhead.

Type enforcement is the second advantage. A Python list will silently accept a string or None mixed in with your integers — that kind of inconsistency might not cause a crash until much later in your program. A python array rejects mismatched types at the exact moment you try to insert them, making bugs easier to find and fix.

Slicing and Iterating a Python Typed Array

Slicing a python array uses the same syntax as slicing a list. The result is always a new array of the same typecode — not a list — so the type enforcement carries through.

import array

pressure = array.array('i', [1012, 1009, 1007, 1005, 1008, 1011, 1014])
early = pressure[:3]
late = pressure[4:]
peak = pressure[-1]

print("Early readings:", early)
print("Late readings:", late)
print("Final reading:", peak)
Early readings: array('i', [1012, 1009, 1007])
Late readings: array('i', [1008, 1011, 1014])
Final reading: 1014

Iterating works exactly as you would expect — standard for loops, enumerate, and list comprehensions all work on a python array without any conversion needed.

import array

heart_rates = array.array('i', [62, 78, 95, 110, 88, 72])

for i, bpm in enumerate(heart_rates):
 zone = "high" if bpm >= 90 else "moderate" if bpm >= 70 else "resting"
 print(f"Sample {i}: {bpm} bpm — {zone}")
Sample 0: 62 bpm — resting
Sample 1: 78 bpm — moderate
Sample 2: 95 bpm — high
Sample 3: 110 bpm — high
Sample 4: 88 bpm — moderate
Sample 5: 72 bpm — moderate

You can also search using index(), which returns the position of the first element that matches a given value:

import array

log_codes = array.array('i', [200, 301, 200, 404, 500, 200])
first_ok = log_codes.index(200)
print("First 200 at index:", first_ok)
First 200 at index: 0

Full Working Example

The following script brings together everything covered — creating a python array, using core array module functions, round-tripping through fromlist() and tolist(), serializing with the python array buffer methods, and comparing memory against a plain list.

import array
import sys

# --- Build a typed array from raw temperature readings ---
temps = array.array('f')
raw_readings = [36.6, 37.1, 36.9, 38.2, 37.5, 36.4, 37.8]
temps.fromlist(raw_readings)

print("=== Temperature Log ===")
print(f"Typecode: {temps.typecode}")
print(f"Bytes per item: {temps.itemsize}")
print(f"Total readings: {len(temps)}")
print(f"Array: {temps}\n")

# --- Modify the array ---
temps.append(38.0) # Add a new reading at the end
temps.insert(0, 36.1) # Prepend a reading
print(f"After append and insert: {temps}\n")

# --- Reverse and slice ---
temps.reverse()
print(f"Reversed: {temps}")
top_three = temps[:3]
print(f"Top 3 highest: {top_three}\n")

# --- Round-trip through a Python list ---
as_list = temps.tolist()
print(f"As Python list: {as_list}")

rebuilt = array.array('f')
rebuilt.fromlist(as_list)
print(f"Rebuilt array: {rebuilt}\n")

# --- Binary buffer round-trip ---
raw_bytes = temps.tobytes()
print(f"Raw bytes (first 8): {raw_bytes[:8]}")
print(f"Total byte length: {len(raw_bytes)}")

restored = array.array('f')
restored.frombytes(raw_bytes)
print(f"Restored array: {restored}\n")

# --- Memory comparison: python array vs list ---
equiv_list = list(temps)
array_bytes = temps.buffer_info()[1] * temps.itemsize
list_bytes = sys.getsizeof(equiv_list)

print("=== Memory Comparison ===")
print(f"Array raw payload: {array_bytes} bytes")
print(f"List object header: {list_bytes} bytes")
print(f"Header savings: {list_bytes - array_bytes} bytes\n")

# --- Type safety enforcement ---
print("=== Type Safety ===")
try:
 temps.append("hot")
except TypeError as e:
 print(f"TypeError caught: {e}")

Output

=== Temperature Log ===
Typecode: f
Bytes per item: 4
Total readings: 7
Array: array('f', [36.599998474121094, 37.099998474121094, 36.900001525878906, 38.20000076293945, 37.5, 36.400001525878906, 37.79999923706055])

After append and insert: array('f', [36.099998474121094, 36.599998474121094, 37.099998474121094, 36.900001525878906, 38.20000076293945, 37.5, 36.400001525878906, 37.79999923706055, 38.0])

Reversed: array('f', [38.0, 37.79999923706055, 36.400001525878906, 37.5, 38.20000076293945, 36.900001525878906, 37.099998474121094, 36.599998474121094, 36.099998474121094])
Top 3 highest: array('f', [38.0, 37.79999923706055, 36.400001525878906])

As Python list: [38.0, 37.79999923706055, 36.400001525878906, 37.5, 38.20000076293945, 36.900001525878906, 37.099998474121094, 36.599998474121094, 36.099998474121094]
Rebuilt array: array('f', [38.0, 37.79999923706055, 36.400001525878906, 37.5, 38.20000076293945, 36.900001525878906, 37.099998474121094, 36.599998474121094, 36.099998474121094])

Raw bytes (first 8): b'\x00\x00\x18B\xcd\xcc\x17B'
Total byte length: 36

Restored array: array('f', [38.0, 37.79999923706055, 36.400001525878906, 37.5, 38.20000076293945, 36.900001525878906, 37.099998474121094, 36.599998474121094, 36.099998474121094])

=== Memory Comparison ===
Array raw payload: 36 bytes
List object header: 184 bytes
Header savings: 148 bytes

=== Type Safety ===
TypeError caught: must be real number, not str