Массивы с заданным типом в Python: полное руководство

Раздел: Структуры данных -> Массивы

Массив типов в Python: обзор и ключевые подходы

Основное эффективное решение: модуль array

Модуль array предоставляет массивы с фиксированным типом элементов. Это наиболее производительный и экономный по памяти способ хранения однотипных числовых данных в стандартной библиотеке Python. Тип задаётся символом (typecode), например, 'i' для знакового целого, 'f' для числа с плавающей точкой. В отличие от списка, массив хранит значения в компактном бинарном виде, что уменьшает потребление памяти и ускоряет операции с большими объёмами данных.

import array

a = array.array('i', [10, 20, 30])
print(a)           # array('i', [10, 20, 30])
print(a.buffer_info())  # (адрес, длина)

Python массив типов (массив типов в python)

Пояснение: после импорта создаётся массив целых чисел (typecode 'i'). Метод buffer_info() возвращает кортеж с адресом буфера в памяти и количеством элементов. Это полезно для низкоуровневых операций.

Цели использования

  • Хранение числовых данных одного типа для экономии памяти.
  • Эффективная работа с бинарными протоколами и файлами.
  • Передача данных в C-расширения или библиотеки (например, через буфер).
  • Замена списков, когда все элементы заведомо одного типа.

Как создать массив с определённым типом в Python?

Для создания массива с типом достаточно вызвать конструктор array.array(typecode, iterable). Доступные typecode: 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'f', 'd', а также символы для беззнаковых и знаковых целых разной длины. Выбор зависит от диапазона значений и требуемой точности.

from array import array

# Целые числа без знака (unsigned short)
a = array('H', [65535, 200])
# Числа с двойной точностью
d = array('d', [3.14, 2.718])

Python массивы типы данных (массивы и типы данных в python)

Типичные ошибки и их решение

  • TypeError: попытка добавить элемент другого типа. Например, a.append(3.14) в массиве 'i' вызовет ошибку. Решение: явно преобразовывать значения, либо использовать подходящий typecode.
  • OverflowError: значение выходит за допустимый диапазон. Например, array('b', [128]) (диапазон 'b' -128..127). Решение: проверять границы или выбрать больший тип.
  • TypeError при инициализации: передача неитерируемого объекта. Решение: передавать список, кортеж или другой итерабельный объект.

В чём разница между array и обычным списком?

Список list может содержать элементы любых типов. Массив array строго типизирован. Это обеспечивает:

  • Меньший расход памяти (до нескольких раз для одинаковых данных).
  • Более высокую скорость при числовых операциях, особенно в цикле.
  • Возможность работы с бинарными данными напрямую.

Однако массив не поддерживает методы, несвойственные числовым последовательностям (например, .index() со сложным условием). Для сложной логики или гетерогенных данных лучше использовать список.

import array, sys

# Сравнение памяти
lst = [i for i in range(1000)]
arr = array.array('i', lst)

print('Размер списка:', sys.getsizeof(lst) + sum(sys.getsizeof(x) for x in lst))
print('Размер массива:', sys.getsizeof(arr) + len(arr) * arr.itemsize)
Размер списка: 40000 (примерно)
Размер массива: 4100 (примерно)

Пример наглядно демонстрирует экономию.

Как использовать numpy для работы с типизированными массивами?

Библиотека numpy предоставляет массивы с явным указанием типа (dtype) и множество векторных операций. Это более мощный, но внешний инструмент. Если проект уже использует numpy, его массивы - естественный выбор для численных расчётов. Создание: np.array([1,2,3], dtype=np.int32).

import numpy as np

a = np.array([1, 2, 3], dtype=np.int32)
print(a.dtype)  # int32
print(a.nbytes) # 12 (3*4)

Возможные проблемы

  • Необходимость устанавливать библиотеку (pip install numpy).
  • Избыточность для простых задач, где достаточно array.

Когда пригодится байтовый массив (bytearray)?

bytearray - изменяемая последовательность байтов (0..255). Используется для работы с бинарными данными, сетевыми протоколами, кодировками. В отличие от array('B'), bytearray не хранит информацию о типе каждого элемента (он всегда байт).

ba = bytearray([0x41, 0x42, 0x43])
ba.append(0x44)
print(ba.decode('ascii'))  # ABCD

Как упаковать и распаковать данные разных типов в массив?

Модуль struct позволяет упаковывать данные в бинарную строку, а затем интерпретировать её как последовательность. Это не массив, но полезно для создания компактных бинарных структур. Для массового хранения одного типа лучше использовать array.

import struct
# Упаковка трёх чисел: int16, float, unsigned char
packed = struct.pack('h f B', 100, 2.5, 200)
# Распаковка
print(struct.unpack('h f B', packed)) # (100, 2.5, 200)

Расширенные примеры работы с массивами типов

Пример 1: конкатенация и повторение массивов

Массивы поддерживают операторы + и *, как списки. Результат - новый массив того же типа.

Пример
from array import array

a = array('i', [1, 2, 3])
b = array('i', [4, 5])
c = a + b         # array('i', [1,2,3,4,5])
d = a * 3         # array('i', [1,2,3,1,2,3,1,2,3])
print(c)
print(d)
array('i', [1, 2, 3, 4, 5])
array('i', [1, 2, 3, 1, 2, 3, 1, 2, 3])

Пример 2: чтение и запись массива в бинарный файл

Методы tofile и fromfile позволяют эффективно сохранять и загружать массивы.

Пример
from array import array

# Запись
arr = array('d', [1.5, 2.7, 3.1])
with open('data.bin', 'wb') as f:
    arr.tofile(f)

# Чтение
loaded = array('d')
with open('data.bin', 'rb') as f:
    loaded.fromfile(f, 3)  # читаем 3 числа
print(loaded)
array('d', [1.5, 2.7, 3.1])

Ошибка: несоответствие размеров

  • Если в файле меньше чисел, чем запрошено, fromfile вызовет EOFError. Решение: читать по частям или использовать frombytes.

Пример 3: использование typecode для беззнаковых 64-битных целых

На 64-битных платформах доступен 'Q' (unsigned long long).

Пример
from array import array

a = array('Q', [2**64-1, 12345678901234567890])
print(a)
print(a.itemsize)  # 8 байт на элемент
array('Q', [18446744073709551615, 12345678901234567890])
8

Пример 4: преобразование массива в байты и обратно

Метод tobytes() возвращает байтовое представление, frombytes() восстанавливает массив.

Пример
from array import array

arr = array('i', [1000, -2000])
b = arr.tobytes()
print(b.hex())

# Восстановление
arr2 = array('i')
arr2.frombytes(b)
print(arr2)
e8030000f0f7ffff
array('i', [1000, -2000])

Обратите внимание: порядок байт зависит от архитектуры (little-endian на x86). Для переносимости используйте struct с явным указанием порядка.

Пример 5: сортировка и поиск в массиве

Массивы поддерживают метод .tolist() для преобразования в список. Встроенных методов сортировки нет, но можно использовать sorted() или отсортировать список и создать новый массив.

Пример
from array import array

a = array('i', [3, 1, 4, 1, 5, 9])
sorted_arr = array('i', sorted(a))
print(sorted_arr)
# Поиск индекса
print(sorted_arr.index(4))
array('i', [1, 1, 3, 4, 5, 9])
3

Пример 6: сравнение скорости операций с массивом и списком

Используем модуль timeit для наглядности.

Пример
import timeit
from array import array

setup = '''
from array import array
lst = list(range(100000))
arr = array('i', lst)
'''

stmt_list = '''
for i in range(len(lst)):
    lst[i] += 1
'''

stmt_arr = '''
for i in range(len(arr)):
    arr[i] += 1
'''

print('Список:', timeit.timeit(stmt_list, setup, number=100))
print('Массив:', timeit.timeit(stmt_arr, setup, number=100))
Список: 1.25 (примерно)
Массив: 0.80 (примерно)

Массив работает быстрее благодаря прямому доступу к памяти и отсутствию упаковки объектов.

Пример 7: работа с массивом символов (typecode 'u')

В Python 3 массив с типом 'u' (Unicode-символ) может хранить строки как последовательность символов. Однако в более новых версиях он считается устаревшим.

Пример
from array import array

char_arr = array('u', 'Привет')
print(char_arr[1])  # р
char_arr.append('!')
print(char_arr.tounicode())  # Привет!
р
Привет!

Предупреждение

  • Начиная с Python 3.3, рекомендуется использовать str или bytearray; array('u') может быть удалён в будущем.

Пример 8: создание многомерного массива с помощью вложенных array

Сам array одномерный. Для многомерности можно использовать список массивов или перейти к numpy.

Пример
from array import array

# Матрица 2x3
matrix = [array('d', [1.0, 2.0, 3.0]),
          array('d', [4.0, 5.0, 6.0])]
print(matrix[0][1])  # 2.0
# Транспонирование с помощью zip
for col in zip(*matrix):
    print(array('d', col))
2.0
array('d', [1.0, 4.0])
array('d', [2.0, 5.0])
array('d', [3.0, 6.0])

Массив типов в Python - comments

En
Python массив типов (python)