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.
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:
| Typecode | C Type | Python Type | Bytes |
|---|---|---|---|
'b' | signed char | int | 1 |
'B' | unsigned char | int | 1 |
'h' | signed short | int | 2 |
'H' | unsigned short | int | 2 |
'i' | signed int | int | 2 |
'l' | signed long | int | 4 |
'L' | unsigned long | int | 4 |
'f' | float | float | 4 |
'd' | double | float | 8 |
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.
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
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() 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() 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(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(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() 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])
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.
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.
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 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
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