Python’s array module stores homogeneous numeric data in a compact C array. from array import array. Constructor: array(typecode, initializer) — typecodes: 'b' signed char, 'B' unsigned char, 'h' signed short, 'H' unsigned short, 'i' signed int, 'I' unsigned int, 'l' signed long, 'L' unsigned long, 'q' signed long long, 'Q' unsigned long long, 'f' float (4 bytes), 'd' double (8 bytes). itemsize: arr.itemsize — bytes per element. typecode: arr.typecode. buffer_info: arr.buffer_info() → (address, length). append: arr.append(x). extend: arr.extend(iterable). insert: arr.insert(i, x). pop: arr.pop(i=-1). remove: arr.remove(x). frombytes/tobytes: binary round-trip. fromfile/tofile: arr.tofile(f) / arr.fromfile(f, n). fromlist/tolist: arr.tolist() → Python list. byteswap: reverse byte order in-place for endianness. array.typecodes — string of all valid typecode chars. Memory: 4–8× more compact than list for numeric data (no per-element Python object header). Works with memoryview: memoryview(arr) for zero-copy slicing. Claude Code generates sensor buffers, audio sample arrays, pixel packed arrays, binary file parsers, and compact numeric accumulators.
CLAUDE.md for array
## array Stack
- Stdlib: from array import array
- Create: arr = array("d", [1.0, 2.0, 3.0]) # double-precision floats
- Binary: arr.tobytes() / array("d", b"...")
- File IO: arr.tofile(f) / arr.fromfile(f, count)
- Interop: arr.tolist() / memoryview(arr) for zero-copy
- Compact: arr.itemsize * len(arr) bytes (no Python object overhead)
array Typed Buffer Pipeline
# app/arrayutil.py — typed arrays, binary IO, sensors, ring buffer, stats
from __future__ import annotations
import math
import struct
import time
from array import array
from collections.abc import Iterable
from dataclasses import dataclass
from pathlib import Path
from typing import TypeVar
T = TypeVar("T")
# ─────────────────────────────────────────────────────────────────────────────
# 1. Factory helpers
# ─────────────────────────────────────────────────────────────────────────────
def float_array(values: Iterable[float] = ()) -> array:
"""
Create a double-precision float array ('d').
Example:
temps = float_array([36.6, 37.1, 36.9, 38.2])
samples = float_array() # empty, append later
"""
return array("d", values)
def int_array(values: Iterable[int] = (), *, signed: bool = True) -> array:
"""
Create a signed ('l') or unsigned ('L') long array.
Example:
counts = int_array([100, 200, 300])
ids = int_array([1, 2, 3], signed=False)
"""
return array("l" if signed else "L", values)
def byte_array(values: Iterable[int] = (), *, signed: bool = False) -> array:
"""
Create a byte array ('b' signed, 'B' unsigned) for raw 8-bit data.
Example:
raw = byte_array(b"hello")
pix = byte_array([0, 128, 255], signed=False)
"""
return array("b" if signed else "B", values)
def short_array(values: Iterable[int] = (), *, signed: bool = True) -> array:
"""
Create a 16-bit integer array ('h' / 'H') — typical for audio PCM samples.
Example:
pcm = short_array(range(44100), signed=True) # 1 sec of silence placeholder
"""
return array("h" if signed else "H", values)
def clone(arr: array) -> array:
"""Return a copy of arr with the same typecode and data."""
return array(arr.typecode, arr)
# ─────────────────────────────────────────────────────────────────────────────
# 2. Binary file I/O
# ─────────────────────────────────────────────────────────────────────────────
def save_array(arr: array, path: str | Path) -> None:
"""
Write arr to a binary file (raw bytes, no header).
Example:
save_array(float_array([1.0, 2.0, 3.0]), "samples.bin")
"""
with open(path, "wb") as f:
arr.tofile(f)
def load_float_array(path: str | Path) -> array:
"""
Load a binary file of 64-bit floats ('d') into an array.
Example:
temps = load_float_array("temps.bin")
"""
path = Path(path)
count = path.stat().st_size // 8 # 'd' is 8 bytes
arr = array("d")
with open(path, "rb") as f:
arr.fromfile(f, count)
return arr
def load_int16_array(path: str | Path) -> array:
"""
Load a binary file of signed 16-bit integers ('h') — e.g., PCM audio.
Example:
pcm = load_int16_array("audio.raw")
"""
path = Path(path)
count = path.stat().st_size // 2
arr = array("h")
with open(path, "rb") as f:
arr.fromfile(f, count)
return arr
def array_to_bytes(arr: array) -> bytes:
"""Serialize arr to raw bytes."""
return arr.tobytes()
def array_from_bytes(typecode: str, data: bytes) -> array:
"""Deserialize raw bytes into an array with the given typecode."""
arr = array(typecode)
arr.frombytes(data)
return arr
def byteswap_copy(arr: array) -> array:
"""Return a copy of arr with byte order swapped (endianness conversion)."""
copy = clone(arr)
copy.byteswap()
return copy
# ─────────────────────────────────────────────────────────────────────────────
# 3. Numeric operations (no numpy — stdlib only)
# ─────────────────────────────────────────────────────────────────────────────
def arr_sum(arr: array) -> float:
"""Return the sum of all elements."""
return sum(arr)
def arr_mean(arr: array) -> float:
"""Return the arithmetic mean."""
if not arr:
return float("nan")
return sum(arr) / len(arr)
def arr_min(arr: array) -> float:
"""Return the minimum value."""
return min(arr)
def arr_max(arr: array) -> float:
"""Return the maximum value."""
return max(arr)
def arr_std(arr: array) -> float:
"""Return the population standard deviation."""
n = len(arr)
if n < 2:
return 0.0
mu = arr_mean(arr)
variance = sum((x - mu) ** 2 for x in arr) / n
return math.sqrt(variance)
def clamp_array(arr: array, lo: float, hi: float) -> array:
"""
Return a new float array with all values clamped to [lo, hi].
Example:
safe = clamp_array(raw_sensor, 0.0, 100.0)
"""
return float_array(max(lo, min(hi, x)) for x in arr)
def scale_array(arr: array, factor: float) -> array:
"""
Return a new float array with all values multiplied by factor.
Example:
normalized = scale_array(pcm, 1.0 / 32768.0)
"""
return float_array(x * factor for x in arr)
def add_arrays(a: array, b: array) -> array:
"""
Element-wise addition of two same-length float arrays.
Example:
mixed = add_arrays(channel_left, channel_right)
"""
if len(a) != len(b):
raise ValueError(f"Length mismatch: {len(a)} vs {len(b)}")
return float_array(x + y for x, y in zip(a, b))
def moving_average(arr: array, window: int) -> array:
"""
Return a float array of simple moving averages with the given window size.
Example:
smoothed = moving_average(sensor_readings, window=5)
"""
result = float_array()
window = min(window, len(arr))
acc = sum(arr[:window])
result.append(acc / window)
for i in range(window, len(arr)):
acc += arr[i] - arr[i - window]
result.append(acc / window)
return result
# ─────────────────────────────────────────────────────────────────────────────
# 4. Ring buffer using array
# ─────────────────────────────────────────────────────────────────────────────
class RingBuffer:
"""
A fixed-capacity ring (circular) buffer backed by a typed array.
Overwrites oldest entries when full. Constant-time append.
Example:
rb = RingBuffer(capacity=4, typecode="d")
for v in [1.0, 2.0, 3.0, 4.0, 5.0]:
rb.push(v)
list(rb) # [2.0, 3.0, 4.0, 5.0] (last 4 values)
"""
def __init__(self, capacity: int, typecode: str = "d") -> None:
if capacity < 1:
raise ValueError("capacity must be >= 1")
self._buf = array(typecode, [0] * capacity)
self._capacity = capacity
self._head = 0
self._size = 0
@property
def capacity(self) -> int:
return self._capacity
@property
def full(self) -> bool:
return self._size == self._capacity
def push(self, value: float) -> None:
"""Append value; overwrites oldest when full."""
self._buf[self._head] = value
self._head = (self._head + 1) % self._capacity
if self._size < self._capacity:
self._size += 1
def peek(self) -> float:
"""Return the most recently pushed value."""
if self._size == 0:
raise IndexError("ring buffer is empty")
idx = (self._head - 1) % self._capacity
return self._buf[idx]
def to_list(self) -> list:
"""Return elements in insertion order (oldest first)."""
if self._size < self._capacity:
return list(self._buf[:self._size])
start = self._head
return [self._buf[(start + i) % self._capacity] for i in range(self._capacity)]
def mean(self) -> float:
if self._size == 0:
return float("nan")
return sum(self.to_list()) / self._size
def __iter__(self):
return iter(self.to_list())
def __len__(self) -> int:
return self._size
def __repr__(self) -> str:
return f"RingBuffer(capacity={self._capacity}, size={self._size}, head={self._head})"
# ─────────────────────────────────────────────────────────────────────────────
# 5. Sensor accumulator
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class SensorBuffer:
"""
Accumulate time-stamped sensor readings into compact arrays.
Example:
buf = SensorBuffer(name="cpu_temp")
buf.record(72.5)
buf.record(73.1)
stats = buf.stats()
print(stats)
"""
name: str
timestamps: array = None # 'd' unix seconds
values: array = None # 'd' float readings
def __post_init__(self) -> None:
self.timestamps = array("d")
self.values = array("d")
def record(self, value: float, ts: float | None = None) -> None:
"""Append a reading with current timestamp (or provided ts)."""
self.timestamps.append(ts if ts is not None else time.time())
self.values.append(value)
def stats(self) -> dict:
n = len(self.values)
if n == 0:
return {"name": self.name, "count": 0}
return {
"name": self.name,
"count": n,
"mean": round(arr_mean(self.values), 4),
"min": arr_min(self.values),
"max": arr_max(self.values),
"std": round(arr_std(self.values), 4),
"bytes": self.values.itemsize * len(self.values),
}
def to_bytes(self) -> bytes:
"""Serialize both arrays to a single bytes blob with a 4-byte count header."""
count = len(self.values)
header = struct.pack(">I", count)
return header + self.timestamps.tobytes() + self.values.tobytes()
@classmethod
def from_bytes(cls, name: str, data: bytes) -> "SensorBuffer":
count = struct.unpack(">I", data[:4])[0]
offset = 4
stride = 8 # 'd' is 8 bytes
ts_data = data[offset:offset + count * stride]
val_data = data[offset + count * stride:offset + 2 * count * stride]
buf = cls(name=name)
buf.timestamps.frombytes(ts_data)
buf.values.frombytes(val_data)
return buf
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import tempfile, os
print("=== array demo ===")
print("\n--- factory helpers ---")
fa = float_array([1.1, 2.2, 3.3, 4.4, 5.5])
ia = int_array([10, 20, 30, 40])
ba = byte_array(range(256))
print(f" float_array typecode={fa.typecode} itemsize={fa.itemsize} len={len(fa)}")
print(f" int_array typecode={ia.typecode} itemsize={ia.itemsize} len={len(ia)}")
print(f" byte_array typecode={ba.typecode} itemsize={ba.itemsize} len={len(ba)}")
print(f" float_array bytes: {fa.itemsize * len(fa)} vs list: {len(fa) * 28} (est)")
print("\n--- numeric ops ---")
print(f" sum={arr_sum(fa):.2f} mean={arr_mean(fa):.2f} min={arr_min(fa):.2f} max={arr_max(fa):.2f} std={arr_std(fa):.4f}")
print("\n--- clamp / scale ---")
clamped = clamp_array(float_array([−5.0, 50.0, 105.0, 200.0]), 0.0, 100.0)
print(f" clamped: {clamped.tolist()}")
scaled = scale_array(fa, 10.0)
print(f" scaled(*10): {scaled.tolist()}")
print("\n--- moving_average ---")
data = float_array([1, 3, 5, 7, 9, 11, 13])
ma = moving_average(data, window=3)
print(f" data: {data.tolist()}")
print(f" MA(3): {ma.tolist()}")
print("\n--- binary IO ---")
with tempfile.TemporaryDirectory() as td:
path = os.path.join(td, "floats.bin")
save_array(fa, path)
loaded = load_float_array(path)
print(f" save/load roundtrip ok: {loaded.tolist() == fa.tolist()}")
raw = array_to_bytes(ia)
restored = array_from_bytes("l", raw)
print(f" bytes roundtrip ok: {restored.tolist() == ia.tolist()}")
print("\n--- byteswap ---")
orig = short_array([256, 512, 1024])
swapped = byteswap_copy(orig)
reswapped = byteswap_copy(swapped)
print(f" orig={orig.tolist()} swapped={swapped.tolist()} re-swapped={reswapped.tolist()}")
print("\n--- RingBuffer ---")
rb = RingBuffer(capacity=4, typecode="d")
for v in [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]:
rb.push(v)
print(f" after 6 pushes into cap=4: {rb.to_list()}")
print(f" peek={rb.peek()} mean={rb.mean():.2f} len={len(rb)}")
print("\n--- SensorBuffer ---")
sensor = SensorBuffer(name="cpu_temp")
import random
for i in range(10):
sensor.record(60.0 + random.gauss(0, 2.5), ts=1_700_000_000.0 + i)
print(f" stats: {sensor.stats()}")
raw_blob = sensor.to_bytes()
restored_sensor = SensorBuffer.from_bytes("cpu_temp", raw_blob)
print(f" roundtrip count ok: {len(restored_sensor.values) == len(sensor.values)}")
print("\n=== done ===")
For the list alternative — Python lists store references to Python objects, giving O(1) random access and arbitrary element types; each element carries full object overhead (~28 bytes for a float); array.array stores C values directly in a flat buffer using only itemsize bytes per element (8 bytes for 'd', 2 for 'h') — use list when elements are mixed types, when you need arbitrary Python objects, or when the collection is small; use array.array when you have millions of homogeneous numeric values and memory compactness or binary file I/O performance matters. For the numpy.ndarray alternative — numpy (PyPI) provides multi-dimensional typed arrays with vectorized math, broadcasting, BLAS-backed linear algebra, and a full ecosystem of scientific libraries; array.array is one-dimensional, has no vectorized arithmetic, and requires explicit loops for numeric operations — use numpy for scientific computing, machine learning, image processing, and any workload where vectorized math, broadcasting, or n-dimensional layout is needed; use array.array for simple 1-D numeric buffers, binary file I/O, inter-process memory sharing via memoryview, and environments where NumPy is unavailable. The Claude Skills 360 bundle includes array skill sets covering float_array()/int_array()/byte_array()/short_array()/clone() factory helpers, save_array()/load_float_array()/array_to_bytes()/array_from_bytes()/byteswap_copy() binary I/O, arr_sum()/arr_mean()/arr_std()/clamp_array()/scale_array()/add_arrays()/moving_average() numeric operations, RingBuffer fixed-capacity circular buffer, and SensorBuffer time-stamped accumulator with binary serialization. Start with the free tier to try compact numeric buffers and array pipeline code generation.