Las listas son flexibles y sirven para la mayoría de las aplicaciones, pero no siempre son la mejor representación para cientos de miles de números del mismo tipo. El módulo estándar array en Python almacena valores numéricos homogéneos en un bloque compacto y contiguo. Cada elemento utiliza una representación binaria fija seleccionada mediante un código de tipo.
Esta estructura puede reducir memoria, facilitar la lectura de archivos binarios y permitir integración con APIs que aceptan el protocolo de buffer. No reemplaza NumPy en cálculo científico, matrices o vectorización, pero resulta útil cuando se busca una solución ligera sin dependencias externas.
Este contenido complementa nuestros artículos sobre bisect, mmap, shelve, filecmp y NumPy.
Cuándo usar array
Usa array.array cuando todos los valores sean compatibles con el mismo tipo numérico de C y necesites una secuencia mutable más compacta que una lista. Algunos casos comunes son muestras de sensores, audio, índices, coordenadas, contadores, registros binarios y bloques recibidos por red.
Una lista guarda referencias a objetos Python. Un array guarda directamente los valores. La diferencia exacta de memoria depende del código de tipo y de la plataforma, por lo que conviene medir con datos reales.
Crear un array
from array import array
valores = array("i", [10, 20, 30, 40])
print(valores)
print(valores[0])El primer argumento es el código de tipo. El código i representa un entero C con signo. Su tamaño exacto puede depender de la plataforma, así que consulta itemsize cuando el formato binario deba ser estable.
Códigos de tipo
Los códigos b y B representan enteros de un byte con y sin signo. h y H representan short, i e I representan int, y q y Q suelen representar valores de 64 bits. Para punto flotante se usan normalmente f y d.
No elijas un código únicamente por su nombre. Comprueba signo, rango y tamaño. Insertar un entero fuera del rango produce OverflowError, evitando una conversión silenciosa.
Agregar y eliminar elementos
datos = array("H")
datos.append(100)
datos.extend([200, 300, 400])
datos.insert(1, 150)
ultimo = datos.pop()
datos.remove(200)La API es parecida a la de una lista e incluye append, extend, insert, pop, remove, reverse, count e index. Todos los elementos deben ser válidos para el tipo seleccionado.
Índices y slicing
muestras = array("h", [2, 4, 6, 8, 10])
print(muestras[1:4])
muestras[1:3] = array("h", [40, 60])Un slice devuelve otro array con el mismo código de tipo. La asignación de slices también requiere un array compatible, lo que evita mezclar representaciones binarias.
Medir el almacenamiento
valores = array("d", [1.5, 2.5, 3.5])
print(valores.itemsize)
print(len(valores) * valores.itemsize)La multiplicación muestra los bytes usados por los elementos, sin incluir el pequeño overhead del contenedor. Al comparar con una lista recuerda que sys.getsizeof() no siempre incluye los objetos numéricos referenciados.
Convertir a listas
lista = valores.tolist()
otro = array("d")
otro.fromlist([4.5, 5.5])tolist() ayuda cuando otra biblioteca espera listas normales. fromlist() valida los valores antes de agregarlos. Con datos externos, realiza conversión y validación de rangos de forma explícita.
Convertir a bytes
numeros = array("I", [1, 2, 3])
bloque = numeros.tobytes()
copia = array("I")
copia.frombytes(bloque)tobytes() exporta la representación nativa. frombytes() requiere que la longitud sea múltiplo de itemsize. Rechaza bloques mal formados antes de procesarlos.
Orden de bytes
La representación nativa depende de la arquitectura. Un archivo little-endian puede interpretarse de forma incorrecta en una máquina big-endian.
import sys
if sys.byteorder != "little":
numeros.byteswap()byteswap() invierte los bytes de cada elemento. Los formatos duraderos y los protocolos deben definir explícitamente orden de bytes, ancho y signo.
Leer y escribir archivos binarios
datos = array("f", [0.5, 1.5, 2.5])
with open("muestras.bin", "wb") as archivo:
datos.tofile(archivo)
cargados = array("f")
with open("muestras.bin", "rb") as archivo:
cargados.fromfile(archivo, 3)fromfile() recibe una cantidad de elementos, no de bytes. Si el archivo termina antes, lanza EOFError, pero algunos elementos pueden haberse agregado. Si necesitas integridad total, descarta el estado parcial.
Protocolo de buffer
Los arrays implementan el protocolo de buffer y pueden exponerse mediante memoryview sin copiar datos.
valores = array("i", [10, 20, 30])
vista = memoryview(valores)
print(vista.format, vista.itemsize)
vista[0] = 99La modificación afecta al array original. Mientras exista una vista exportada, las operaciones que cambian el tamaño pueden fallar. Libera la vista antes de agregar o insertar elementos.
Interoperabilidad sin copias
Extensiones nativas, sockets, funciones de compresión, hashes y APIs del sistema suelen aceptar objetos compatibles con buffer. Pasar el array directamente puede evitar una copia intermedia.
La compatibilidad de buffer no garantiza que ambas partes interpreten los datos igual. Deben coincidir tamaño, alineación, signo, orden de bytes y longitud.
Validar rangos
pequeno = array("B")
pequeno.append(255)
# pequeno.append(256) # OverflowErrorValida valores controlados por usuarios antes de insertarlos. Para enteros, comprueba límites. Para floats, define cómo tratar NaN, infinito, cero negativo y pérdida de precisión.
Array frente a list
Elige una lista cuando los elementos sean heterogéneos, necesites objetos Python o valores anidados. Elige array para números homogéneos, buffers compactos y formatos binarios.
Un array no acelera automáticamente los bucles de Python. Sumar o transformar elemento por elemento sigue ejecutando operaciones del intérprete.
Array frente a NumPy
NumPy ofrece matrices multidimensionales, broadcasting, álgebra lineal y operaciones vectorizadas. El módulo array es más pequeño, viene con Python y sirve para almacenamiento numérico unidimensional.
Para análisis de datos, imágenes o matrices grandes, NumPy suele ser mejor. Para utilidades ligeras o lectores binarios simples, array puede ser suficiente.
Mutabilidad y concurrencia
Los arrays son mutables y no incluyen sincronización de alto nivel. Si varios threads o procesos modifican los mismos datos, utiliza locks o un protocolo explícito. Una memoryview no vuelve atómicas las actualizaciones compuestas.
Errores frecuentes
- Elegir un código sin revisar
itemsize. - Guardar datos nativos cuando el formato exige endianness fijo.
- Confundir elementos con bytes.
- Insertar valores fuera del rango.
- Redimensionar con vistas activas.
- Esperar vectorización como en NumPy.
- Confiar en bloques binarios externos sin validarlos.
Buenas prácticas
- Documenta tipo, signo, ancho y orden de bytes.
- Valida rangos antes de insertar.
- Usa context managers para archivos.
- Comprueba múltiplos de
itemsize. - Libera vistas antes de redimensionar.
- Mide memoria y rendimiento con datos reales.
- Migra a NumPy cuando necesites cálculo vectorizado.
Conclusión
El módulo array en Python proporciona secuencias numéricas compactas y mutables. Permite convertir directamente a bytes, trabajar con archivos binarios y compartir memoria mediante el protocolo de buffer.
Su uso seguro depende de elegir el tipo correcto, validar rangos y definir un formato portátil. Consulta la documentación oficial de array y la documentación del protocolo de buffer.







