Массивы с заданным типом в 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])