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

Что такое двумерные массивы, как ими пользоваться и зачем

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

Двумерный массив — это структура данных, которая хранит информацию в виде таблицы строк и столбцов. Это как шахматная доска бесконечных размеров: у каждой ячейки есть номер, а внутри она хранит какое-то значение, например ячейка пуста или занята фигурой.

В программировании такие массивы используют для разных целей: работы с таблицами, изображениями (потому что пиксели тоже двумерный массив) или игровыми полями (как в «Крестиках-ноликах»). 

Сегодня рассказываем, как создать такой массив в языке программирования Python и управлять им.

Введение в двумерные массивы

Чтобы понять массивы, нужно сначала вспомнить, что такое списки.

Списки в Python — упорядоченная изменяемая коллекция элементов любого типа, допустимо даже смешивать типы между собой. Списки можно изменять по ходу выполнения программы, их не нужно пересоздавать и выделять память отдельно.

Списки выделяются квадратными скобками, элементы разделяются запятой:

my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

В качестве элементов списка могут быть другие списки.

Двумерный массив Python реализуется как раз как список списков. Пример такого массива — таблица температур за неделю:

# строки — дни недели; столбцы — утро, день, вечер 
temperature = [ 
   # понедельник:  
   [12, 18, 15],
   # вторник:
   [10, 16, 14],
   # среда:
   [11, 17, 13]    
] 

Получается, что значения лежат как бы на пересечении строки и столбца. Например, 18 градусов находится на пересечении столбца «День» и строки «Понедельник», а 13 градусов — на пересечении «Вечер» и «Среда».

Способы создания двумерных массивов

В Python есть несколько способов создать двумерный массив. Самый простой — вручную перечислить все элементы. Но если массив большой, удобнее заполнить его автоматически с помощью циклов или генераторов списков.

Для начала можно создать шаблон-болванку: таблицу умножения или пустое поле для игры, где все клетки заполнены нулями. После этого каждый элемент можно заменить на тот, что действительно нужен в реальной программе. При этом нужно не забыть, что каждая строка — отдельный список внутри основного списка.

Создание массива с использованием цикла:

# создание массива 3×3, заполненного нулями
matrix = []
for i in range(3):
   # создаём строку из трёх нулей
   row = [0] * 3
   # добавляем строку в матрицу
   matrix.append(row)

# выводим массив построчно с отступами
for row in matrix:
   print(' '.join(map(str, row)))

Другой вариант создания массива — через генератор списков:

# [0] * 3 создаёт список из 3 нулей
# _ - элемент итерируемого объекта или диапазона
# range(3) - диапазон из 3 повторений
matrix = [[0] * 3 for _ in range(3)]

# выводим массив построчно с отступами
for row in matrix:
  print(' '.join(map(str, row)))

Оба варианта выведут одно и то же. Мы добавили аккуратное форматирование при выводе, поэтому будет так:

0 0 0
0 0 0
0 0 0

Самая частая ошибка при создании массивов

Может показаться, что есть ещё один простой способ создания массива: создать массив из элементов, количество которых будет равно количеству столбцов. А потом умножить этот массив на количество строк.

С нулями это будет выглядеть так:

wrong_array = [[0] * 3] * 3

В чём тут проблема: мы создаём один список из 3 нулей, а потом 3 раза дублируем на него ссылку. В итоге получается, что все строки в памяти будут представлять собой один и тот же список, поэтому, если изменить элемент в одной строке, он изменится во всех остальных.

Сейчас наш массив выводит то же самое, что и предыдущие:

0 0 0
0 0 0
0 0 0

Теперь попробуем заменить первый ноль на другое число:

# меняем первый ноль в первой строке
wrong_array[0][0] = 7
# выводим массив построчно с отступами
for row in wrong_array:
   print(' '.join(map(str, row)))

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

7 0 0
7 0 0
7 0 0

Генераторы списков и циклы создают отдельные списки для каждой строки, поэтому такой способ правильный и безопасный.

Инициализация двумерного массива

Инициализация — это процесс задания начальных значений массива. Можно заполнить его:

  • Одинаковыми значениями. В наших примерах мы заполнили массивы нулями. 
  • Последовательными числами. Например, для тестовых данных.
  • Случайными числами. Может понадобиться для генерации карты в игре.

Заполнить массив конкретными значениями можно как вручную, так и автоматически. Вот пример ручной инициализации и через генератор списков:

# инициализация вручную
matrix = [ 
   [1, 2], 
   [3, 4] 
] 
for row in matrix:
   print(' '.join(map(str, row)))

# добавляем пустую строку между выводами
print()

# инициализация через генератор списка.
# каждый элемент считается по формуле так,
# чтобы все числа шли по порядку
matrix = [[i * 3 + j for j in range(3)] for i in range(3)]
# выводим массив построчно с отступами
for row in matrix:
   print(' '.join(map(str, row)))

При запуске получаем два варианта массива:

1 2
3 4

0 1 2
3 4 5
6 7 8

Способы ввода двумерных массивов

Данные в массиве можно задавать несколькими способами:

  • Вручную, то есть прописать значения прямо в коде. Это подходит только для небольших массивов.
  • С клавиатуры — попросить пользователя ввести числа.
  • Из файла — считать готовую таблицу из CSV или текстового файла.

Для примера напишем небольшую программу, в которой пользователь будет сам вводить элементы массива с клавиатуры. Ещё добавим обработку ошибок и подробные указания, что и когда вводить.

Сначала запрашиваем количество строк и столбцов:

# запрашиваем у пользователя количество строк
print("Введите количество строк матрицы (n):")
# преобразуем введённую строку в целое число
n = int(input())

# запрашиваем у пользователя количество столбцов
print("Введите количество столбцов матрицы (m):")
# преобразуем введённую строку в целое число
m = int(input())

Создаём пустую матрицу, двумерный массив:

# инициализируем пустую матрицу
matrix = []

Реализуем построчный ввод. Для этого попросим пользователя вводить элементы матрицы через пробел:

print(f"\nВведите {n} строк матрицы, в каждой строке {m} чисел, разделённых пробелами:")

После каждой строки нужно нажимать Enter.

Для такого ввода нам понадобится цикл. На каждой итерации принимаем от пользователя строку, разбиваем её по пробелам, преобразуем в числа и создаём из этих чисел список. Этот список будет являться строкой  будущего массива.

# считываем матрицу построчно
for i in range(n):
   # цикл для повторного ввода при ошибке
   while True:
       try:
           print(f"Строка {i + 1}: ", end="")
           # читаем строку, разбиваем по пробелам, преобразуем в числа
           row = list(map(int, input().split()))

После этого проверяем, что пользователь ввёл нужное количество чисел — то есть количество столбцов, которые он указал в начале:

# проверяем, что введено правильное количество чисел
if len(row) != m:
   print(f"Ошибка! Должно быть {m} чисел. Попробуйте ещё раз.")
   continue

Если количество чисел неправильное, выдаём ошибку и запускаем цикл снова. Если всё верно, добавляем список в массив:

  # добавляем строку в матрицу
   matrix.append(row)
   # выходим из цикла при успешном вводе
   break

except ValueError:
   print("Ошибка! Вводите только числа, разделённые пробелами. Попробуйте ещё раз.")

Выводим форматированный результат на экран:

# выводим результат
print("\nПолученная матрица:")
for i in range(n):
   for j in range(m):
       # форматированный вывод с отступами
       print(f"{matrix[i][j]:5}", end=" ")
   # переход на новую строку после вывода строки матрицы
   print()

Смотрим, как это работает:

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

Способы вывода двумерных массивов

Как можно вывести двумерный массив на экран:

  • Целиком — напечатать все строки подряд. Тогда матрица будет отображена в одну строку, то есть все одномерные списки будут идти через запятую.
  • Поэлементно — обойти вложенными циклами. В этом варианте двумерный массив будет выглядеть как таблица. Списки могут быть в квадратных скобках, а могут без — это зависит от кода.
  • Выборочно — только определённые строки или столбцы.

Самый простой вывод всей матрицы поэлементно выглядит так:

# создаём двумерный массив через генератор списков
dimensional = [[i * 3 + j for j in range(3)] for i in range(3)]

# выводим заголовок для вывода
print("\nМатрица:\n")
for row in dimensional:
   # каждая строка выводится в отдельной строке
   print(row)

Что на экране при запуске:

Матрица:

[0, 1, 2]
[3, 4, 5]
[6, 7, 8]

Вывод определённых столбца и строки массива

Ещё раз вспомним, чем являются строки и столбцы двумерных массивов:

  • Столбец — это все элементы с одинаковым индексом в разных строках. 
  • Строка — это один из вложенных списков. 

Возьмём для примера наш массив с температурами:

# строки — дни недели; столбцы — утро, день, вечер 
temperature = [ 
   # понедельник:  
   [12, 18, 15],
   # вторник:
   [10, 16, 14],
   # среда:
   [11, 17, 13]    
] 

Столбец 1 — это все дневные температуры, потому что отсчёт начинается с 0:

# вывод второго столбца (индекс 1)  
print("Второй столбец:")  
for row in temperature:  
    print(row[1]) 

Вывод:

Второй столбец:

18

16

17

Первая строка — это данные за понедельник:

# вывод первой строки (индекс 0)  
print("Первая строка:")  
print(temperature[0])

Запускаем код:

Первая строка:

[12, 18, 15]

Обработка элементов двумерного массива

Двумерные массивы можно не только создавать и выводить, но и сортировать, изменять, удалять элементы. Это можно использовать для анализа данных в таблицах или обновления игрового поля.

Все двумерные массивы просто наборы списков, поэтому и их обработка совпадает с теми же операциями над списками. Питон предоставляет удобные встроенные механизмы для этой работы даже без таких сложных дополнительных библиотек, как NumPy.

Вот несколько примеров. Работать будем с таким массивом:

matrix = [
    [5, 2, 8],
    [1, 4, 3],
    [9, 6, 7]
]

Сортировка строк двумерного массива

Это позволяет упорядочить данные в массиве. Например, отсортировать оценки студентов по возрастанию или таблицу товаров по цене.

Сортируем первую строку:

# сортируем первую строку (индекс 0) по возрастанию
matrix[0].sort()
# выводим на экран
print("Массив с отсортированной первой строкой:")
for row in matrix:
   print(' '.join(map(str, row)))

После сортировки:

Массив с отсортированной первой строкой:

Массив с отсортированной первой строкой:
2 5 8
1 4 3
9 6 7

Обновление значений в двумерном массиве

Иногда нужно изменить конкретный элемент или целую строку. Например, обновить цену товара в таблице или состояние пикселя в изображении.

Меняем элемент в одной строке:

# меняем значение во второй строке, третьем столбце (индексы 1, 2)
matrix[1][2] = 99
# выводим на экран
print("Массив с изменённым значением во второй строке и третьем столбце:")
for row in matrix:
   print(' '.join(map(str, row)))

Обновлённый массив:

Массив с изменённым значением во второй строке и третьем столбце:
2 5 8
1 4 99
9 6 7

Удаление значений из двумерного массива

Можно удалять отдельные элементы или целые строки. Например, если товар закончился, его можно убрать из таблицы.

Удаляем элемент:

# удаляем второй элемент из первой строки (индекс 1)
del matrix[0][1]
# выводим на экран
print("Массив с удалённым значением в первой строке:")
for row in matrix:
   print(' '.join(map(str, row)))

Запускаем код:

Массив с удалённым значением в первой строке:
2 8
1 4 99
9 6 7

Примеры обработки двумерного массива

Вот ещё два примера, что можно делать с массивами.

Находим максимальный элемент:

# поиск максимального элемента
max_value = max(max(row) for row in matrix)
print("Максимальное значение:", max_value)

Проверяем, что нашла функция:

Максимальное значение: 99

Считаем сумму всех элементов:

# сумма всех элементов
total = sum(sum(row) for row in matrix)
print("Сумма всех элементов:", total)

Считаем сумму:

Сумма всех элементов: 136

Навигация по двумерным массивам

Для работы с двумерными массивами нужно получать доступ к его элементам и перебирать их. Каждый элемент имеет два индекса:

  • Первый — номер строки.
  • Второй — номер столбца.

Отсчёт обоих индексов начинается с 0. То есть первый элемент первой строки будет иметь индексы [0][0].

Для практики создадим такой массив:

# создаём массив
matrix = [
   ["A", "B", "C"],
   ["D", "E", "F"],
   ["G", "H", "I"]
]

Получаем доступ к конкретному элементу:

# получаем элемент во второй строке, третьем столбце (индексы 1, 2)
element = matrix[1][2]
print(element)

Проверяем, что выводится в консоли:

F

А вот как различаются способы вывода массива. Выведем массив дважды — как список списков и просто как значения элементов:

# выводим массив с обозначением элементов
for row in matrix:
   print(row)

# выводим массив поэлементно — только значения
for row in matrix:
   for element in row:
       # при выводе разделяем значения пробелами
       print(element, end=" ")

Теперь сразу понятно, как различается внешний вид вывода:

['A', 'B', 'C']
['D', 'E', 'F']
['G', 'H', 'I']
A B C
D E F
G H I

Вам слово

Приходите к нам в соцсети поделиться своим мнением о двумерных массивах в Python и почитать, что пишут другие. А ещё там выходит дополнительный контент, которого нет на сайте — шпаргалки, опросы и разная дурка. В общем, вот тележка, вот ВК — велком!

Обложка:

Алексей Сухов

Корректор:

Александр Зубов

Вёрстка:

Егор Степанов

Соцсети:

Юлия Зубарева

Вам может быть интересно
easy