Работа со значениями в массивах Python: от списков до NumPy
Основной способ: списки Python (list)
Как хранить и получать значения по порядковому номеру?
Список в Python является универсальной коллекцией для хранения любых объектов. Для доступа к элементу используется индекс в квадратных скобках. Индексация начинается с 0.
fruits = ['apple', 'banana', 'cherry']
print(fruits[0]) # apple
print(fruits[1]) # banana
print(fruits[-1]) # cherry (отрицательный индекс)Python значение массива (значение массива в python)
apple banana cherry
Для изменения значения достаточно присвоить новое по индексу:
fruits[1] = 'blueberry'
print(fruits)['apple', 'blueberry', 'cherry']
Цель
Обеспечить простой и быстрый доступ к элементам коллекции по их позиции. Используется в большинстве скриптов для работы с однородными или разнородными данными.
Ошибка IndexError возникает при обращении к несуществующему индексу. Всегда проверяйте длину списка с помощью len().
nums = [1, 2, 3]
# print(nums[5]) # IndexError: list index out of range
if len(nums) > 5:
print(nums[5])Как проверить наличие значения в списке и найти его индекс?
Для проверки существует оператор in, а для получения индекса - метод index().
colors = ['red', 'green', 'blue']
print('green' in colors) # True
print('yellow' in colors) # False
print(colors.index('green')) # 1
# print(colors.index('yellow')) # ValueErrorTrue False 1
Цель и случаи использования
Быстрая проверка принадлежности элемента (оператор in работает за O(n) в среднем). Метод index применяется, когда нужно узнать позицию первого вхождения. Полезно при работе с уникальными идентификаторами или конфигурациями.
Метод index() вызывает ValueError, если элемент отсутствует. Перед вызовом следует использовать in или обернуть вызов в try/except.
if 'yellow' in colors:
idx = colors.index('yellow')
else:
idx = -1 # или NoneКак преобразовать все значения списка по правилу без циклов?
Для этого применяется генератор списка (list comprehension) или функция map(). Они компактнее и быстрее явного цикла.
numbers = [1, 2, 3, 4, 5]
squares = [x**2 for x in numbers]
print(squares) # [1, 4, 9, 16, 25]
# С условием
even_squares = [x**2 for x in numbers if x % 2 == 0]
print(even_squares) # [4, 16][1, 4, 9, 16, 25] [4, 16]
Аналог с map():
squares_map = list(map(lambda x: x**2, numbers))
print(squares_map)[1, 4, 9, 16, 25]
Цель и случаи использования
Когда нужно быстро изменить каждый элемент списка (нормализация, преобразование типов, фильтрация). List comprehension читается и выполняется эффективнее, чем цикл for с append.
При работе с большими списками list comprehension создаёт новый список в памяти. Если требуется экономия памяти, используют генераторное выражение (круглые скобки) и обрабатывают элементы по одному.
large_range = range(10**7)
squares_gen = (x**2 for x in large_range) # генератор, не хранит все
print(next(squares_gen)) # 0
print(next(squares_gen)) # 1Как работать с однородными числовыми данными эффективнее, чем списки?
Для числовых массивов фиксированного типа используются модуль array (встроенный) и библиотека NumPy. Они экономят память и позволяют выполнять векторные операции.
Пример с array:
import array
arr = array.array('i', [1, 2, 3, 4]) # 'i' - знаковое целое
arr.append(5)
print(arr[0]) # 1
print(arr) # array('i', [1, 2, 3, 4, 5])Пример с NumPy:
import numpy as np
np_arr = np.array([1, 2, 3, 4], dtype=np.float64)
print(np_arr * 2) # [2. 4. 6. 8.]
print(np_arr.mean()) # 2.5[2. 4. 6. 8.] 2.5
Цель и случаи использования
array подходит для больших числовых последовательностей, когда нужна только эффективная память и простой доступ. NumPy - для научных расчётов, статистики, линейной алгебры, где важна скорость операций над массивами.
Стандартный список можно ошибочно использовать для хранения огромных числовых данных - это приводит к большому расходу памяти. Для array и NumPy требуется импорт модуля, и операции с ними не поддерживают произвольные типы объектов.
Дополнительные приёмы работы со значениями массивов
Копирование списков: поверхностное и глубокое
Присваивание не создаёт копию, а только ссылку. Чтобы получить независимый список, используется срез [:], метод copy() или модуль copy для сложных вложенных структур.
original = [[1, 2], [3, 4]]
shallow = original[:] # поверхностная копия
shallow[0][0] = 99
print(original) # [[99, 2], [3, 4]] - изменён!
import copy
deep = copy.deepcopy(original)
deep[0][0] = 0
print(original) # [[99, 2], [3, 4]] - не изменился[[99, 2], [3, 4]] [[99, 2], [3, 4]]
Для одномерных списков достаточно copy() или [:].
Удаление дубликатов с сохранением порядка
Если важен порядок, используется проход с помощью dict.fromkeys() или OrderedDict (в современных версиях Python достаточно обычного dict).
items = [1, 2, 2, 3, 1, 4]
unique = list(dict.fromkeys(items))
print(unique) # [1, 2, 3, 4][1, 2, 3, 4]
Этот способ быстрее, чем проверка вхождения во временный список.
Бинарный поиск в отсортированном списке
Для больших отсортированных списков модуль bisect предоставляет эффективный поиск с логарифмической сложностью.
import bisect
sorted_list = [1, 3, 5, 7, 9]
pos = bisect.bisect_left(sorted_list, 5) # 2, элемент найден
pos2 = bisect.bisect_left(sorted_list, 4) # 2, место для вставки
print(pos, pos2)2 2
Полезно для диапазонных запросов и поддержания отсортированного списка.
Использование collections.deque как двусторонней очереди
Для добавления и удаления элементов с обоих концов deque эффективнее списка.
from collections import deque
dq = deque([1, 2, 3])
dq.appendleft(0)
dq.append(4)
print(dq) # deque([0, 1, 2, 3, 4])
dq.popleft() # 0
dq.pop() # 4deque([0, 1, 2, 3, 4])
Векторизация вычислений с NumPy
NumPy выполняет операции над массивами на уровне C, что значительно быстрее циклов Python.
import numpy as np
big = np.arange(10**6) # массив от 0 до 999999
# Умножение каждого элемента на 2 (векторизованная операция)
result = big * 2
print(result[:5]) # первые 5 элементов: [0 2 4 6 8][0 2 4 6 8]
Также доступны универсальные функции (ufunc), агрегации и операции над осями.
Ошибки при изменении списка во время итерации
Не следует удалять или добавлять элементы в список, одновременно проходя по нему циклом for. Это приводит к пропуску элементов или индексам вне границ.
lst = [1, 2, 3, 4, 5]
# Неправильно: for x in lst: lst.remove(x)
# Правильный подход - итерация по копии:
for x in lst[:]:
if x % 2 == 0:
lst.remove(x)
print(lst) # [1, 3, 5][1, 3, 5]
Или использование спискового включения для фильтрации.