El módulo array proporciona un tipo de dato especializado array.array para almacenar secuencias de números homogéneos. A diferencia de la list universal, los arrays de array.array ofrecen un uso más eficiente de la memoria y un mayor rendimiento al trabajar con datos numéricos.
📦 Ventajas clave de array.array
La diferencia clave entre array.array y list es el almacenamiento compacto de datos. En lugar de una lista de punteros a objetos de Python, array.array almacena los valores como un bloque contiguo de bytes, lo que lo hace ideal para las siguientes tareas.
1. Ahorro de memoria al trabajar con grandes conjuntos de números
Al procesar millones de elementos numéricos, el ahorro de memoria se vuelve de vital importancia. array.array reduce significativamente la sobrecarga.
import array
import sys
def compare_memory_usage(num_elements: int = 1_000_000) -> None:
"""
Compara el uso de memoria entre list y array.array.
Args:
num_elements (int, optional): Número de elementos para la prueba.
Por defecto es 1,000,000.
"""
# Crear una lista con objetos enteros de Python
list_numbers = list(range(num_elements))
# Crear un array donde los números se almacenan como enteros de 4 bytes tipo C
array_numbers = array.array('i', range(num_elements))
list_size = sys.getsizeof(list_numbers)
array_size = sys.getsizeof(array_numbers)
print(f"Número de elementos: {num_elements}")
print(f"Tamaño de list: {list_size / 1024 / 1024:.2f} MB")
print(f"Tamaño de array: {array_size / 1024 / 1024:.2f} MB")
if array_size > 0:
print(f"Ahorro de memoria: {list_size / array_size:.2f}x")
# Ejemplo de uso
if __name__ == "__main__":
compare_memory_usage()
Salida:
Número de elementos: 1000000
Tamaño de list: 7.63 MB
Tamaño de array: 3.82 MB
Ahorro de memoria: 2.00x```
---
### 2. Mayor rendimiento en operaciones numéricas
Debido a la asignación contigua de memoria, las operaciones matemáticas sobre los elementos de `array.array` se realizan más rápido, ya que el procesador puede utilizar la caché de manera más eficiente.
python
import array
import timeit
def compare_performance(num_elements: int = 10_000_000) -> None:
«»»
Compara el rendimiento de la suma de elementos en list y array.array.
Args:
num_elements (int, optional): Número de elementos para la prueba.
Por defecto es 10,000,000.
"""
setup_code = f"""
import array
data = range({num_elements})
list_data = list(data)
array_data = array.array(‘i’, data)
«»»
# Medir el tiempo para la lista
list_time = timeit.timeit("sum(list_data)", setup=setup_code, number=10)
# Medir el tiempo para el array
array_time = timeit.timeit("sum(array_data)", setup=setup_code, number=10)
print(f"Tiempo para sumar {num_elements} elementos (10 veces):")
print(f"list: {list_time:.4f} segundos")
print(f"array: {array_time:.4f} segundos")
Ejemplo de uso
if name == «main«:
compare_performance()
**Salida:**
Tiempo para sumar 10000000 elementos (10 veces):
list: 2.1106 segundos
array: 1.1549 segundos
---
### 3. Trabajo directo con bibliotecas de C (`ctypes`, `struct`)
`array.array` es ideal para pasar datos a bibliotecas de bajo nivel escritas en C, ya que su estructura interna es compatible con los arrays de C.
#### Ejemplo con `ctypes`:
python
import array
from ctypes import c_double, CDLL
def demonstrate_ctypes_usage() -> None:
«»»
Demuestra cómo pasar un array.array a una función de C a través de ctypes.
«»»
# Array con números de doble precisión (tipo ‘d’)
py_array = array.array(‘d’, [1.1, 2.2, 3.3, 4.4])
# Crear un array compatible con C a partir de py_array
# La función (c_double * len(py_array)) crea un tipo "array de 4 c_double"
# (*py_array) desempaqueta el array de Python en los argumentos de este constructor
c_array = (c_double * len(py_array))(*py_array)
# Aquí podría haber una llamada a una función de C, por ejemplo:
# my_c_library = CDLL("./libmath.so")
# my_c_library.sum_doubles(c_array, len(c_array))
print(f"Array de Python: {py_array}")
print(f"Array compatible con C (ctypes): {[val for val in c_array]}")
Ejemplo de uso
if name == «main«:
demonstrate_ctypes_usage()
#### Ejemplo con `struct` para empaquetado de datos:
python
import array
import struct
def demonstrate_struct_packing(data: list[int]) -> bytes:
«»»
Empaqueta un array de enteros en una cadena binaria.
Args:
data (list[int]): Lista de enteros para empaquetar.
Returns:
bytes: Representación binaria de los datos.
"""
arr = array.array('i', data)
# Crear una cadena de formato como '3i' para 3 enteros
format_string = f'{len(arr)}i'
# Empaquetar datos en formato binario
binary_data = struct.pack(format_string, *arr)
print(f"Array original: {arr}")
print(f"Datos binarios: {binary_data}")
# Comprobación: desempaquetar de nuevo
unpacked_data = struct.unpack(format_string, binary_data)
print(f"Datos desempaquetados: {unpacked_data}")
return binary_data
Ejemplo de uso
if name == «main«:
demonstrate_struct_packing([10, 20, 30])
---
### 4. Serialización y deserialización eficientes
Los métodos `.tobytes()` y `.frombytes()` permiten convertir rápidamente un array a bytes y viceversa, lo que es ideal para guardar en archivos o transmitir a través de la red.
python
import array
def handle_binary_data() -> None:
«»»
Demuestra la serialización y deserialización de array.array a bytes.
«»»
# Crear el array original
source_array = array.array(‘i’, [1, 2, 3, 4, 5])
print(f»Array original: {source_array}»)
# Serializar el array a bytes
binary_data = source_array.tobytes()
print(f"Datos en bytes: {binary_data}")
# Deserializar de bytes a un nuevo array
new_array = array.array('i')
new_array.frombytes(binary_data)
print(f"Array restaurado: {new_array}")
# Comprobar la integridad
assert source_array == new_array, "¡Los datos no coinciden!"
print("Integridad de los datos confirmada.")
Ejemplo de uso
if name == «main«:
handle_binary_data()«`
5. Garantía de homogeneidad de tipo
array.array impone estrictamente un solo tipo de dato, especificado en su creación. Esto previene la adición accidental de elementos de un tipo diferente.
import array
def demonstrate_type_safety() -> None:
"""
Muestra que array.array no permite añadir elementos de un tipo diferente.
"""
arr = array.array('i', [100, 200, 300])
print(f"Array de enteros: {arr}")
try:
# Intentar añadir un elemento de tipo cadena (string)
arr.append('hello')
except TypeError as e:
# Excepción esperada
print(f"\nEl intento de añadir 'hello' generó un error: {e}")
print("Esto confirma el tipado estricto del array.")
# Ejemplo de uso
if __name__ == "__main__":
demonstrate_type_safety()
6. Escritura y lectura directa desde archivos binarios
Los métodos .tofile() y .fromfile() simplifican el trabajo con archivos binarios, evitando la serialización intermedia.
import array
from pathlib import Path
def work_with_binary_files(file_path_str: str = "data.bin") -> None:
"""
Escribe un array en un archivo binario y lo lee de vuelta.
Args:
file_path_str (str, optional): Nombre del archivo para guardar.
Por defecto es "data.bin".
"""
file_path = Path(file_path_str)
source_array = array.array('f', [1.5, 2.7, 3.14])
try:
# Escribir en el archivo
with file_path.open('wb') as f:
source_array.tofile(f)
print(f"Array {source_array} escrito en el archivo '{file_path}'.")
# Leer desde el archivo
new_array = array.array('f')
with file_path.open('rb') as f:
# Leer 3 elementos de tipo 'f' (float)
new_array.fromfile(f, len(source_array))
print(f"Array {new_array} leído desde el archivo.")
assert source_array == new_array
finally:
# Eliminación garantizada del archivo después de la ejecución
if file_path.exists():
file_path.unlink()
print(f"Archivo temporal '{file_path}' eliminado.")
# Ejemplo de uso
if __name__ == "__main__":
work_with_binary_files()
🔹 Tabla comparativa: array.array vs list
| Característica | array.array | list |
|---|---|---|
| Tipo de datos | Primitivos homogéneos (números, caracteres) | Cualquier objeto de Python |
| Memoria | Bajo consumo | Alto consumo |
| Rendimiento | Alto para operaciones numéricas | Menor para operaciones numéricas |
| API | Conjunto de métodos limitado | API rica y flexible |
| Compatibilidad con C | Alta, transferencia directa de datos | Requiere conversiones |
| Serialización binaria | Métodos incorporados (.tobytes, .tofile) | Requiere struct, pickle, etc. |
Conclusión:
🚀 Usa array.array cuando trabajes con grandes volúmenes de datos numéricos homogéneos, y cuando el rendimiento y el uso eficiente de la memoria sean críticos para ti.
Para la mayoría de las tareas cotidianas donde se requiere flexibilidad y el almacenamiento de datos heterogéneos, list sigue siendo la mejor opción.