Aller au contenu
> 💻 🧠 Code 1001 > 🧪📓 Sans catégorie > 🧑‍💻 Utilisation de array.array en Python : quand et pourquoi l’appliquer

🧑‍💻 Utilisation de array.array en Python : quand et pourquoi l’appliquer


📦 Principaux avantages de array.array

La principale différence entre array.array et list est le stockage compact des données. Au lieu d’une liste de pointeurs vers des objets Python, array.array stocke les valeurs sous la forme d’un bloc d’octets contigu, ce qui le rend idéal pour les tâches suivantes.


1. Économie de mémoire lors du traitement de grands ensembles de nombres

Lors du traitement de millions d’éléments numériques, les économies de mémoire deviennent d’une importance capitale. array.array réduit considérablement la surcharge.

import array
import sys

def compare_memory_usage(num_elements: int = 1_000_000) -> None:
    """
    Compare l'utilisation de la mémoire entre list et array.array.

    Args:
        num_elements (int, optional): Nombre d'éléments pour le test. 
                                      La valeur par défaut est 1 000 000.
    """
    # Créer une liste avec des objets entiers Python
    list_numbers = list(range(num_elements))

    # Créer un tableau où les nombres sont stockés en tant qu'entiers de type C de 4 octets
    array_numbers = array.array('i', range(num_elements))

    list_size = sys.getsizeof(list_numbers)
    array_size = sys.getsizeof(array_numbers)

    print(f"Nombre d'éléments: {num_elements}")
    print(f"Taille de la liste: {list_size / 1024 / 1024:.2f} Mo")
    print(f"Taille du tableau: {array_size / 1024 / 1024:.2f} Mo")
    if array_size > 0:
        print(f"Économie de mémoire: {list_size / array_size:.2f}x")

# Exemple d'utilisation
if __name__ == "__main__":
    compare_memory_usage()```
**Sortie :**

Nombre d’éléments: 1000000
Taille de la liste: 7.63 Mo
Taille du tableau: 3.82 Mo
Économie de mémoire: 2.00x

---

### 2. Performances accrues des opérations numériques

En raison de l'allocation de mémoire contiguë, les opérations mathématiques sur les éléments de `array.array` sont effectuées plus rapidement, car le processeur peut utiliser le cache plus efficacement.

python
import array
import timeit

def compare_performance(num_elements: int = 10_000_000) -> None:
«  » »
Compare les performances de la sommation des éléments dans list et array.array.

Args:
    num_elements (int, optional): Nombre d'éléments pour le test. 
                                  La valeur par défaut est 10 000 000.
"""
setup_code = f"""

import array
data = range({num_elements})
list_data = list(data)
array_data = array.array(‘i’, data)
«  » »

# Mesurer le temps pour la liste
list_time = timeit.timeit("sum(list_data)", setup=setup_code, number=10)

# Mesurer le temps pour le tableau
array_time = timeit.timeit("sum(array_data)", setup=setup_code, number=10)

print(f"Temps pour sommer {num_elements} éléments (10 fois):")
print(f"list:  {list_time:.4f} secondes")
print(f"array: {array_time:.4f} secondes")

Exemple d’utilisation

if name == « main« :
compare_performance()

**Sortie :**

Temps pour sommer 10000000 éléments (10 fois):
list: 2.1106 secondes
array: 1.1549 secondes

---

### 3. Travail direct avec les bibliothèques C (`ctypes`, `struct`)

`array.array` est idéal pour passer des données à des bibliothèques de bas niveau écrites en C, car sa structure interne est compatible avec les tableaux C.

#### Exemple avec `ctypes` :

python
import array
from ctypes import c_double, CDLL

def demonstrate_ctypes_usage() -> None:
«  » »
Démontre le passage de array.array à une fonction C via ctypes.
«  » »
# Tableau avec des nombres à double précision (type ‘d’)
py_array = array.array(‘d’, [1.1, 2.2, 3.3, 4.4])

# Créer un tableau compatible C à partir de py_array
# La fonction (c_double * len(py_array)) crée un type "tableau de 4 c_double"
# (*py_array) décompresse le tableau python dans les arguments de ce constructeur
c_array = (c_double * len(py_array))(*py_array)

# Ici pourrait se trouver un appel à une fonction C, par exemple :
# my_c_library = CDLL("./libmath.so")
# my_c_library.sum_doubles(c_array, len(c_array))

print(f"Tableau Python: {py_array}")
print(f"Tableau compatible C (ctypes): {[val for val in c_array]}")

Exemple d’utilisation

if name == « main« :
demonstrate_ctypes_usage()

#### Exemple avec `struct` pour le paquetage des données :

python
import array
import struct

def demonstrate_struct_packing(data: list[int]) -> bytes:
«  » »
Empaquette un tableau d’entiers dans une chaîne binaire.

Args:
    data (list[int]): Liste d'entiers à empaqueter.

Returns:
    bytes: Représentation binaire des données.
"""
arr = array.array('i', data)

# Créer une chaîne de format comme '3i' pour 3 entiers
format_string = f'{len(arr)}i'

# Empaqueter les données au format binaire
binary_data = struct.pack(format_string, *arr)

print(f"Tableau original: {arr}")
print(f"Données binaires: {binary_data}")

# Vérification : décompresser à nouveau
unpacked_data = struct.unpack(format_string, binary_data)
print(f"Données décompressées: {unpacked_data}")

return binary_data

Exemple d’utilisation

if name == « main« :
demonstrate_struct_packing([10, 20, 30])

---

### 4. Sérialisation et désérialisation efficaces

Les méthodes `.tobytes()` et `.frombytes()` permettent de convertir rapidement un tableau en octets et inversement, ce qui est idéal pour l'enregistrement dans des fichiers ou la transmission sur un réseau.

python
import array

def handle_binary_data() -> None:
«  » »
Démontre la sérialisation et la désérialisation de array.array en octets.
«  » »
# Créer le tableau source
source_array = array.array(‘i’, [1, 2, 3, 4, 5])
print(f »Tableau original: {source_array} »)

# Sérialiser le tableau en octets
binary_data = source_array.tobytes()
print(f"Données en octets: {binary_data}")

# Désérialiser des octets vers un nouveau tableau
new_array = array.array('i')
new_array.frombytes(binary_data)
print(f"Tableau restauré: {new_array}")

# Vérifier l'intégrité
assert source_array == new_array, "Les données ne correspondent pas !"
print("Intégrité des données confirmée.")

Exemple d’utilisation

if name == « main« :
handle_binary_data()

---

### 5. Garantie de l'homogénéité des types

`array.array` impose strictement un seul type de données, spécifié lors de sa création. Cela empêche l'ajout accidentel d'éléments d'un type différent.

python
import array

def demonstrate_type_safety() -> None:
«  » »
Montre que array.array n’autorise pas l’ajout d’éléments d’un type différent.
«  » »
arr = array.array(‘i’, [100, 200, 300])
print(f »Tableau d’entiers: {arr} »)

try:
    # Tentative d'ajout d'un élément de type chaîne de caractères
    arr.append('hello')
except TypeError as e:
    # Exception attendue
    print(f"\nLa tentative d'ajout de 'hello' a provoqué une erreur: {e}")
    print("Cela confirme le typage strict du tableau.")

Exemple d’utilisation

if name == « main« :
demonstrate_type_safety()

---

### 6. Écriture et lecture directes depuis des fichiers binaires

Les méthodes `.tofile()` et `.fromfile()` simplifient le travail avec les fichiers binaires, en évitant la sérialisation intermédiaire.

python
import array
from pathlib import Path

def work_with_binary_files(file_path_str: str = « data.bin ») -> None:
«  » »
Écrit un tableau dans un fichier binaire et le relit.

Args:
    file_path_str (str, optional): Nom du fichier pour la sauvegarde.
                                   La valeur par défaut est "data.bin".
"""
file_path = Path(file_path_str)
source_array = array.array('f', [1.5, 2.7, 3.14])

try:
    # Écrire dans le fichier
    with file_path.open('wb') as f:
        source_array.tofile(f)
    print(f"Tableau {source_array} écrit dans le fichier '{file_path}'.")

    # Lire depuis le fichier
    new_array = array.array('f')
    with file_path.open('rb') as f:
        # Lire 3 éléments de type 'f' (float)
        new_array.fromfile(f, len(source_array))
    print(f"Tableau {new_array} lu depuis le fichier.")

    assert source_array == new_array

finally:
    # Suppression garantie du fichier après exécution
    if file_path.exists():
        file_path.unlink()
        print(f"Fichier temporaire '{file_path}' supprimé.")

Exemple d’utilisation

if name == « main« :
work_with_binary_files()
« `


🔹 Tableau comparatif : array.array vs list

Caractéristiquearray.arraylist
Type de donnéesPrimitifs homogènes (nombres, caractères)N’importe quel objet Python
MémoireFaible consommationForte consommation
PerformanceÉlevée pour les opérations numériquesPlus faible pour les opérations numériques
APIEnsemble de méthodes limitéAPI riche et flexible
Compatibilité CÉlevée, transfert direct des donnéesNécessite des conversions
Sérialisation binaireMéthodes intégrées (.tobytes, .tofile)Nécessite struct, pickle, etc.

Conclusion :

🚀 Utilisez array.array lorsque vous travaillez avec de grands volumes de données numériques homogènes, et lorsque les performances et l’utilisation efficace de la mémoire sont essentielles pour vous.

Pour la plupart des tâches quotidiennes où la flexibilité et le stockage de données hétérogènes sont requis, list reste le meilleur choix.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *