Перейти до вмісту
> 💻 🧠 Код 1001 > 🧪📓 Без категорії > 🧑‍💻 Використання array.array в Python: коли та навіщо застосовувати

🧑‍💻 Використання array.array в Python: коли та навіщо застосовувати


📦 Ключові переваги array.array

Ключова відмінність array.array від list — компактне зберігання даних. Замість списку вказівників на об’єкти Python, array.array зберігає значення як суцільний блок байтів, що робить його ідеальним для таких завдань.


1. Економія пам’яті при роботі з великими наборами чисел

При обробці мільйонів числових елементів економія пам’яті стає критично важливою. array.array значно зменшує накладні витрати.

import array
import sys

def compare_memory_usage(num_elements: int = 1_000_000) -> None:
    """
    Порівнює використання пам'яті між list та array.array.

    Args:
        num_elements (int, optional): Кількість елементів для тесту. 
                                      За замовчуванням 1 000 000.
    """
    # Створити список з цілочисельними об'єктами Python
    list_numbers = list(range(num_elements))

    # Створити масив, де числа зберігаються як 4-байтні цілі числа типу C
    array_numbers = array.array('i', range(num_elements))

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

    print(f"Кількість елементів: {num_elements}")
    print(f"Розмір list:  {list_size / 1024 / 1024:.2f} МБ")
    print(f"Розмір array: {array_size / 1024 / 1024:.2f} МБ")
    if array_size > 0:
        print(f"Економія пам'яті: {list_size / array_size:.2f}x")

# Приклад використання
if __name__ == "__main__":
    compare_memory_usage()

Вивід:

Кількість елементів: 1000000
Розмір list:  7.63 МБ
Розмір array: 3.82 МБ
Економія пам'яті: 2.00x

2. Підвищена продуктивність числових операцій

Завдяки суцільному розподілу пам’яті математичні операції над елементами array.array виконуються швидше, оскільки процесор може ефективніше використовувати кеш.

import array
import timeit

def compare_performance(num_elements: int = 10_000_000) -> None:
    """
    Порівнює продуктивність підсумовування елементів у list та array.array.

    Args:
        num_elements (int, optional): Кількість елементів для тесту. 
                                      За замовчуванням 10 000 000.
    """
    setup_code = f"""
import array
data = range({num_elements})
list_data = list(data)
array_data = array.array('i', data)
"""

    # Виміряти час для списку
    list_time = timeit.timeit("sum(list_data)", setup=setup_code, number=10)

    # Виміряти час для масиву
    array_time = timeit.timeit("sum(array_data)", setup=setup_code, number=10)

    print(f"Час на підсумовування {num_elements} елементів (10 разів):")
    print(f"list:  {list_time:.4f} секунд")
    print(f"array: {array_time:.4f} секунд")

# Приклад використання
if __name__ == "__main__":
    compare_performance()

Вивід:

Час на підсумовування 10000000 елементів (10 разів):
list:  2.1106 секунд
array: 1.1549 секунд

3. Пряма робота з бібліотеками C (ctypes, struct)

array.array ідеально підходить для передачі даних у низькорівневі бібліотеки, написані на C, оскільки його внутрішня структура сумісна з масивами C.

Приклад з ctypes:

import array
from ctypes import c_double, CDLL

def demonstrate_ctypes_usage() -> None:
    """
    Демонструє передачу array.array у C-функцію через ctypes.
    """
    # Масив із числами подвійної точності (тип 'd')
    py_array = array.array('d', [1.1, 2.2, 3.3, 4.4])

    # Створити C-сумісний масив з py_array
    # Функція (c_double * len(py_array)) створює тип "масив з 4 c_double"
    # (*py_array) розпаковує масив python в аргументи цього конструктора
    c_array = (c_double * len(py_array))(*py_array)

    # Тут міг би бути виклик C-функції, наприклад:
    # my_c_library = CDLL("./libmath.so")
    # my_c_library.sum_doubles(c_array, len(c_array))

    print(f"Масив Python: {py_array}")
    print(f"C-сумісний масив (ctypes): {[val for val in c_array]}")

# Приклад використання
if __name__ == "__main__":
    demonstrate_ctypes_usage()

Приклад з struct для пакування даних:

import array
import struct

def demonstrate_struct_packing(data: list[int]) -> bytes:
    """
    Пакує масив цілих чисел у бінарний рядок.

    Args:
        data (list[int]): Список цілих чисел для пакування.

    Returns:
        bytes: Бінарне представлення даних.
    """
    arr = array.array('i', data)

    # Створити рядок форматування, наприклад '3i' для 3 цілих чисел
    format_string = f'{len(arr)}i'

    # Запакувати дані в бінарний формат
    binary_data = struct.pack(format_string, *arr)

    print(f"Оригінальний масив: {arr}")
    print(f"Бінарні дані: {binary_data}")

    # Перевірка: розпакувати назад
    unpacked_data = struct.unpack(format_string, binary_data)
    print(f"Розпаковані дані: {unpacked_data}")

    return binary_data

# Приклад використання
if __name__ == "__main__":
    demonstrate_struct_packing([10, 20, 30])

4. Ефективна серіалізація та десеріалізація

Методи .tobytes() та .frombytes() дозволяють швидко перетворювати масив у байти і назад, що ідеально підходить для збереження у файли або передачі по мережі.

import array

def handle_binary_data() -> None:
    """
    Демонструє серіалізацію та десеріалізацію array.array у байти.
    """
    # Створити вихідний масив
    source_array = array.array('i', [1, 2, 3, 4, 5])
    print(f"Вихідний масив: {source_array}")

    # Серіалізувати масив у байти
    binary_data = source_array.tobytes()
    print(f"Дані в байтах: {binary_data}")

    # Десеріалізувати з байтів у новий масив
    new_array = array.array('i')
    new_array.frombytes(binary_data)
    print(f"Відновлений масив: {new_array}")

    # Перевірити цілісність
    assert source_array == new_array, "Невідповідність даних!"
    print("Цілісність даних підтверджено.")

# Приклад використання
if __name__ == "__main__":
    handle_binary_data()

5. Гарантія однорідності типів

array.array суворо вимагає використання лише одного типу даних, зазначеного при створенні. Це запобігає випадковому додаванню елементів іншого типу.

import array

def demonstrate_type_safety() -> None:
    """
    Показує, що array.array не дозволяє додавати елементи іншого типу.
    """
    arr = array.array('i', [100, 200, 300])
    print(f"Цілочисельний масив: {arr}")

    try:
        # Спроба додати рядковий елемент
        arr.append('hello')
    except TypeError as e:
        # Очікуваний виняток
        print(f"\nСпроба додати 'hello' викликала помилку: {e}")
        print("Це підтверджує сувору типізацію масиву.")

# Приклад використання
if __name__ == "__main__":
    demonstrate_type_safety()

6. Прямий запис та читання з бінарних файлів

Методи .tofile() та .fromfile() спрощують роботу з бінарними файлами, уникаючи проміжної серіалізації.

import array
from pathlib import Path

def work_with_binary_files(file_path_str: str = "data.bin") -> None:
    """
    Записує масив у бінарний файл і читає його назад.

    Args:
        file_path_str (str, optional): Ім'я файлу для збереження.
                                       За замовчуванням "data.bin".
    """
    file_path = Path(file_path_str)
    source_array = array.array('f', [1.5, 2.7, 3.14])

    try:
        # Записати у файл
        with file_path.open('wb') as f:
            source_array.tofile(f)
        print(f"Масив {source_array} записано у файл '{file_path}'.")

        # Прочитати з файлу
        new_array = array.array('f')
        with file_path.open('rb') as f:
            # Прочитати 3 елементи типу 'f' (float)
            new_array.fromfile(f, len(source_array))
        print(f"Масив {new_array} прочитано з файлу.")

        assert source_array == new_array

    finally:
        # Гарантоване видалення файлу після виконання
        if file_path.exists():
            file_path.unlink()
            print(f"Тимчасовий файл '{file_path}' видалено.")

# Приклад використання
if __name__ == "__main__":
    work_with_binary_files()

🔹 Порівняльна таблиця: array.array vs list

Характеристикаarray.arraylist
Тип данихОднорідні примітиви (числа, символи)Будь-які об’єкти Python
Пам’ятьНизьке споживанняВисоке споживання
ПродуктивністьВисока для числових операційНижча для числових операцій
APIОбмежений набір методівБагатий та гнучкий API
Сумісність із CВисока, пряма передача данихПотрібні перетворення
Бінарна серіалізаціяВбудовані методи (.tobytes, .tofile)Вимагає struct, pickle тощо

Висновок:

🚀 Використовуйте array.array, коли працюєте з великими обсягами однорідних числових даних, і коли для вас критично важливі продуктивність та ефективне використання пам’яті.

Для більшості повсякденних завдань, де потрібна гнучкість і зберігання різнорідних даних, list залишається найкращим вибором.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *